<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://dev.anandamakaranda.in/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Vaishnavi</id>
	<title>Anandamakaranda - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://dev.anandamakaranda.in/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Vaishnavi"/>
	<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/Special:Contributions/Vaishnavi"/>
	<updated>2026-10-03T02:49:36Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.5</generator>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7602</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7602"/>
		<updated>2026-09-30T13:15:36Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            var label = teekaLabel(g);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; label) h.textContent = label;&lt;br /&gt;
                            var slug = el.getAttribute(&#039;data-teeka-slug&#039;);&lt;br /&gt;
                            if (slug &amp;amp;&amp;amp; label) {&lt;br /&gt;
                                labelBySlug[slug] = label;&lt;br /&gt;
                                // A footnote section for this commentary may&lt;br /&gt;
                                // already be on screen; rename it too.&lt;br /&gt;
                                document.querySelectorAll(&lt;br /&gt;
                                    &#039;.gr-teeka-notes-group[data-teeka-slug=&amp;quot;&#039; + slug +&lt;br /&gt;
                                    &#039;&amp;quot;] .gr-teeka-notes-title&#039;).forEach(function (t) {&lt;br /&gt;
                                        t.textContent = label;&lt;br /&gt;
                                    });&lt;br /&gt;
                            }&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont, limit) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: limit, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                // The API answers 200 with an {error:...} body, so a failed&lt;br /&gt;
                // query is not a rejected promise. Treat it as one.&lt;br /&gt;
                if (j &amp;amp;&amp;amp; j.error) throw new Error(j.error.code || &#039;api error&#039;);&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) {&lt;br /&gt;
                    return page(j[&#039;continue&#039;].rccontinue, limit);&lt;br /&gt;
                }&lt;br /&gt;
                return all;&lt;br /&gt;
            }).catch(function (err) {&lt;br /&gt;
                // recentchanges over a date range is the most expensive query&lt;br /&gt;
                // this site makes, and on a busy or constrained database it&lt;br /&gt;
                // can come back as a DBQueryError. Halve the window and retry&lt;br /&gt;
                // once; failing that, return nothing. A &amp;quot;what&#039;s new&amp;quot; list is&lt;br /&gt;
                // not worth breaking the page for.&lt;br /&gt;
                if (limit &amp;gt; 100) return page(cont, 100);&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[gr] recent changes unavailable: &#039; +&lt;br /&gt;
                                 (err &amp;amp;&amp;amp; err.message || err));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null, 500);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([&lt;br /&gt;
            fetchChanges().catch(function () { return []; }),&lt;br /&gt;
            fetchCatalogue().catch(function () { return null; })&lt;br /&gt;
        ]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = (window.grLibrary &amp;amp;&amp;amp; window.grLibrary.labelFor(g.slug))&lt;br /&gt;
                            || g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // ---- CONFIGURE ME -------------------------------------------------&lt;br /&gt;
    // Where the Grantha library&#039;s REST API lives. Same-origin by default,&lt;br /&gt;
    // which needs no CORS - but the library is its own app, so unless it is&lt;br /&gt;
    // reverse-proxied under the wiki&#039;s domain this must be its real host:&lt;br /&gt;
    //     var API_BASE = &#039;https://library.example.org/api/granthas&#039;;&lt;br /&gt;
    // On another host the library must send&lt;br /&gt;
    //     Access-Control-Allow-Origin: https://anandamakaranda.in&lt;br /&gt;
    // or the browser will refuse the reply and every citation will fall&lt;br /&gt;
    // back to the author baked into the page (usually none).&lt;br /&gt;
    var API_BASE = window.GR_LIBRARY_API || &#039;/api/granthas&#039;;&lt;br /&gt;
    // Set to false while the library is not yet reachable: no request is&lt;br /&gt;
    // made at all, and citations use whatever the page itself carries.&lt;br /&gt;
    // Flip it back to true once API_BASE resolves.&lt;br /&gt;
    var LIBRARY_ENABLED = true;&lt;br /&gt;
    // -------------------------------------------------------------------&lt;br /&gt;
    var API = API_BASE.replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var mem = {};&lt;br /&gt;
    var warned = false;&lt;br /&gt;
    // Once the library has answered &amp;quot;not here&amp;quot;, every further call will get&lt;br /&gt;
    // the same answer. Stop asking: a wrong or missing API base otherwise&lt;br /&gt;
    // fills the network log with one 404 per work on the page.&lt;br /&gt;
    var libraryDown = !LIBRARY_ENABLED;&lt;br /&gt;
&lt;br /&gt;
    // In memory only, for the life of the page. Names used to be kept in&lt;br /&gt;
    // sessionStorage for six hours, which quietly outlived any correction&lt;br /&gt;
    // made in the library or on the map page. They now come from the map&lt;br /&gt;
    // page, which is read once per load, so there is nothing to gain from&lt;br /&gt;
    // a second cache that can disagree with it.&lt;br /&gt;
    function cacheGet(id) { return mem[id] || null; }&lt;br /&gt;
    function cacheSet(id, v) { mem[id] = v; }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        // The map page carries the names for every work this wiki uses, so&lt;br /&gt;
        // normally nothing is fetched from the library at all. That matters&lt;br /&gt;
        // because the library is someone else&#039;s server: we cannot add CORS&lt;br /&gt;
        // headers to it, and a cross-origin call we cannot configure is a&lt;br /&gt;
        // call that will not work. The live API stays as an enhancement for&lt;br /&gt;
        // whenever it IS reachable.&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var w = (m.works || {})[id];&lt;br /&gt;
            if (w &amp;amp;&amp;amp; (w.name || w.nameIAST)) {&lt;br /&gt;
                var v = { id: id, name: w.name || &#039;&#039;, nameIAST: w.nameIAST || &#039;&#039;,&lt;br /&gt;
                          author: w.author || &#039;&#039;, authorIAST: w.authorIAST || &#039;&#039;,&lt;br /&gt;
                          category: w.category || &#039;&#039;, prasthana: w.prasthana || &#039;&#039; };&lt;br /&gt;
                cacheSet(id, v);&lt;br /&gt;
                return v;&lt;br /&gt;
            }&lt;br /&gt;
            return fetchFromLibrary(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchFromLibrary(id) {&lt;br /&gt;
        if (libraryDown) return Promise.resolve(null);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            // A 404 or 5xx means the base URL is wrong or the service is&lt;br /&gt;
            // down - not that this one work is missing - so trip the breaker.&lt;br /&gt;
            if (!r.ok) {&lt;br /&gt;
                if (r.status === 404 || r.status &amp;gt;= 500) libraryDown = true;&lt;br /&gt;
                throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            }&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function (err) {&lt;br /&gt;
            // Offline, blocked by CORS, wrong host, or the id is not in the&lt;br /&gt;
            // library yet. The page&#039;s own data-author/data-doc-title stand&lt;br /&gt;
            // in, so a citation is never blocked on this request - but an&lt;br /&gt;
            // unreachable library looks exactly like a work with no author,&lt;br /&gt;
            // so say which it is, once.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            libraryDown = true;&lt;br /&gt;
            if (!warned) {&lt;br /&gt;
                warned = true;&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[grantha] could not read the library at &#039; + API +&lt;br /&gt;
                        &#039; (asked for &amp;quot;&#039; + id + &#039;&amp;quot;): &#039; + (err &amp;amp;&amp;amp; err.message || err) +&lt;br /&gt;
                        &#039;. Citations will use whatever the page itself carries. &#039; +&lt;br /&gt;
                        &#039;Set window.GR_LIBRARY_API to the library host, and allow &#039; +&lt;br /&gt;
                        &#039;this origin via Access-Control-Allow-Origin.&#039;);&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            }&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        // Deliberately NOT cached in sessionStorage. It was, for six hours,&lt;br /&gt;
        // which meant that editing the map page changed nothing for anyone&lt;br /&gt;
        // whose tab had already read the old one - the exact failure you hit&lt;br /&gt;
        // while setting this up. mapPromise already collapses it to one&lt;br /&gt;
        // request per page load, and the browser&#039;s own HTTP cache handles&lt;br /&gt;
        // the rest; a stale map is far more expensive than a small fetch.&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr-lib:map&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages, works,&lt;br /&gt;
                // locators} when names and division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m };&lt;br /&gt;
                m.works = m.works || {};&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try {&lt;br /&gt;
                    if (!Object.keys(m.works).length) {&lt;br /&gt;
                        console.info(&#039;[grantha] the map page has no &amp;quot;works&amp;quot; section, &#039; +&lt;br /&gt;
                            &#039;so names must come from the library API. Re-run &#039; +&lt;br /&gt;
                            &#039;sync_grantha_map.py to embed them.&#039;);&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                // On a commentary&#039;s own page the heading still reads as the&lt;br /&gt;
                // docx colophon (&amp;quot;...विरचिता प्रकाशिका&amp;quot;). Name it the way the&lt;br /&gt;
                // blocks are named instead.&lt;br /&gt;
                if (t.getAttribute(&#039;data-parent-doc&#039;)) {&lt;br /&gt;
                    var lab = teekaLabel(g);&lt;br /&gt;
                    if (lab) t.textContent = lab;&lt;br /&gt;
                } else if (g.name) {&lt;br /&gt;
                    t.textContent = g.name;&lt;br /&gt;
                }&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (libraryDown) return Promise.resolve({});&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
              if (!r.ok) { libraryDown = true; return null; }&lt;br /&gt;
              return r.json();&lt;br /&gt;
          })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        // lookup() answers from the map page without a network call, so&lt;br /&gt;
        // resolving fifty works costs one shared map fetch, not fifty&lt;br /&gt;
        // requests. Only ids the map does not name reach the library.&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // How a commentary is named on screen: its author, then the work.&lt;br /&gt;
    // &amp;quot;श्रीवादिराजतीर्थः: काठकोपनिषद्भाष्यप्रकाशिका&amp;quot; - because several&lt;br /&gt;
    // commentaries on one moola share a work-type name (प्रकाशिका,&lt;br /&gt;
    // विवरणम्, खण्डार्थः), and the author is what actually tells them&lt;br /&gt;
    // apart for a reader scanning down the page.&lt;br /&gt;
    var labelBySlug = {};&lt;br /&gt;
    function teekaLabel(g) {&lt;br /&gt;
        if (!g) return &#039;&#039;;&lt;br /&gt;
        var name = g.name || g.nameIAST || &#039;&#039;;&lt;br /&gt;
        var author = g.author || g.authorIAST || &#039;&#039;;&lt;br /&gt;
        if (!author || !name) return name || author;&lt;br /&gt;
        // Nothing to prefix if the work already opens with the author.&lt;br /&gt;
        if (name.indexOf(author) === 0) return name;&lt;br /&gt;
        return author + &#039;: &#039; + name;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         teekaLabel: teekaLabel,&lt;br /&gt;
                         labelFor: function (slug) { return labelBySlug[slug] || &#039;&#039;; },&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7601</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7601"/>
		<updated>2026-09-30T12:07:15Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  /* CHANGED from inline-flex + vertical-align:middle. An inline-flex box&lt;br /&gt;
     aligned &amp;quot;middle&amp;quot; is positioned by its own height against the parent&#039;s&lt;br /&gt;
     x-height, so the whole control - checkbox AND label together - sat&lt;br /&gt;
     progressively lower than the text beside it as the reader enlarged the&lt;br /&gt;
     type (measured: 1.8px out at 16px, 3.6px at 30px). Left as plain&lt;br /&gt;
     inline, the label is ordinary text and lines up with its neighbours by&lt;br /&gt;
     definition; only the checkbox needs aligning, and it does that with an&lt;br /&gt;
     em-based offset that holds at any size. */&lt;br /&gt;
  display: inline;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  /* Sized in em so it keeps pace with A- / A+ rather than staying a fixed&lt;br /&gt;
     15px box against text that grows; clamped so it stays a sensible&lt;br /&gt;
     target at both extremes.&lt;br /&gt;
     Alignment was measured rather than guessed. vertical-align:middle is&lt;br /&gt;
     the only value that stays consistent as the type size changes (it is&lt;br /&gt;
     computed from the font&#039;s x-height), but it sits about 2.5px low&lt;br /&gt;
     against this line-height at every size - so that constant bias is&lt;br /&gt;
     taken back with `top`, giving under a pixel of error from 16px to&lt;br /&gt;
     30px. The element is already position:relative for its tick. */&lt;br /&gt;
  width: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  height: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  min-width: 13px; min-height: 13px;&lt;br /&gt;
  margin: 0 6px 0 0; padding: 0; cursor: pointer;&lt;br /&gt;
  vertical-align: middle; top: -2.5px;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  /* Placed and sized in % of the box, so the tick follows the em-based&lt;br /&gt;
     box instead of staying a 4x8px mark inside a checkbox that grew. */&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 32%; top: 8%;&lt;br /&gt;
  width: 26%; height: 55%;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The annotation count badge was removed (it was clipped by the round&lt;br /&gt;
   toggle&#039;s own overflow). This keeps it hidden for anyone still served a&lt;br /&gt;
   cached copy of the old gadget. */&lt;br /&gt;
#gra-toggle-badge { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── LINK LANDING ───────────────────────────────────────────────────&lt;br /&gt;
   Where a copied id or a bookmark citation link lands. The flash used to&lt;br /&gt;
   be injected by copyId.js; it lives here so the landing still reads when&lt;br /&gt;
   only common.js is loaded. */&lt;br /&gt;
.gid-target {&lt;br /&gt;
  animation: gid-target-flash 2.4s ease-out 1;&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
}&lt;br /&gt;
@keyframes gid-target-flash {&lt;br /&gt;
  0%, 55% { background: rgba(181,69,27,0.14); box-shadow: 0 0 0 4px rgba(181,69,27,0.10); }&lt;br /&gt;
  100%    { background: transparent; box-shadow: none; }&lt;br /&gt;
}&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .gid-target { animation: none; background: rgba(181,69,27,0.14); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── BOOKMARK CITATION ──────────────────────────────────────────────&lt;br /&gt;
   The reference a bookmark produces, shown on its card and copyable in&lt;br /&gt;
   one click. Set small and in the UI face deliberately: it is apparatus&lt;br /&gt;
   for the reader to take away, not part of the text. */&lt;br /&gt;
.gra-bookmark-cite {&lt;br /&gt;
  margin-top: 6px;&lt;br /&gt;
  /* --font-translit, not --font-indic. The reference is romanised - IAST&lt;br /&gt;
     author, IAST title, Latin site name and URL - and Gentium Book Plus&lt;br /&gt;
     is the only family here with a REAL italic face (Adishila, which&lt;br /&gt;
     --font-indic leads with, has none at this weight, so the italic title&lt;br /&gt;
     fell back to a synthetic slant or to nothing at all). Gentium&#039;s&lt;br /&gt;
     unicode-range covers Latin only, so a Devanagari fallback title still&lt;br /&gt;
     lands on Adishila further down the stack. */&lt;br /&gt;
  font-family: var(--font-translit);&lt;br /&gt;
  font-synthesis: style;            /* allow a slant for the Devanagari case */&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.54), 15px);&lt;br /&gt;
  line-height: 1.5; color: #6b5b52;&lt;br /&gt;
  /* Ragged right and flush left. The hanging indent that was here reads&lt;br /&gt;
     correctly in a printed reference LIST, where it separates one entry&lt;br /&gt;
     from the next; on a single card it just looked like the lines had&lt;br /&gt;
     slipped. Justification came in from the panel and stretched the word&lt;br /&gt;
     gaps, which is what made the spacing look broken. */&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  text-indent: 0;&lt;br /&gt;
  padding-left: 0;&lt;br /&gt;
  overflow-wrap: break-word;&lt;br /&gt;
  word-break: normal;&lt;br /&gt;
  hyphens: none;&lt;br /&gt;
}&lt;br /&gt;
.gra-bookmark-cite a {&lt;br /&gt;
  color: inherit; text-decoration: none; border-bottom: 1px dotted #c3b3a9;&lt;br /&gt;
  overflow-wrap: anywhere;          /* only the URL may break mid-token */&lt;br /&gt;
}&lt;br /&gt;
.gra-bookmark-cite a:hover { color: #b5451b; border-bottom-color: #b5451b; }&lt;br /&gt;
.gra-bookmark-cite i,&lt;br /&gt;
.gra-bookmark-cite em { font-style: italic; font-synthesis: style; }&lt;br /&gt;
&lt;br /&gt;
/* Where the bookmark sits in the text. A caption, not part of the&lt;br /&gt;
   citation - so it is set in the UI face and kept visually apart from&lt;br /&gt;
   the reference below it. */&lt;br /&gt;
.gra-bookmark-locator {&lt;br /&gt;
  margin-top: 5px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(10px, calc(var(--gr-font-size, 22px) * 0.48), 13px);&lt;br /&gt;
  font-weight: 600;&lt;br /&gt;
  letter-spacing: .02em;&lt;br /&gt;
  color: #8a7a70;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gra-cite-actions { display: flex; flex-wrap: wrap; gap: 6px; }&lt;br /&gt;
&lt;br /&gt;
.gra-cite-copy {&lt;br /&gt;
  margin-top: 6px; padding: 3px 10px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  background: #fff5f2; border: 1px solid #f0cfc4; border-radius: 20px;&lt;br /&gt;
  cursor: pointer; transition: background .15s, color .15s, border-color .15s;&lt;br /&gt;
}&lt;br /&gt;
.gra-cite-copy:hover { background: #b5451b; color: #fff; border-color: #b5451b; }&lt;br /&gt;
.gra-cite-copy--done { background: #2e6e40; color: #fff; border-color: #2e6e40; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7600</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7600"/>
		<updated>2026-09-30T12:01:31Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  /* CHANGED from inline-flex + vertical-align:middle. An inline-flex box&lt;br /&gt;
     aligned &amp;quot;middle&amp;quot; is positioned by its own height against the parent&#039;s&lt;br /&gt;
     x-height, so the whole control - checkbox AND label together - sat&lt;br /&gt;
     progressively lower than the text beside it as the reader enlarged the&lt;br /&gt;
     type (measured: 1.8px out at 16px, 3.6px at 30px). Left as plain&lt;br /&gt;
     inline, the label is ordinary text and lines up with its neighbours by&lt;br /&gt;
     definition; only the checkbox needs aligning, and it does that with an&lt;br /&gt;
     em-based offset that holds at any size. */&lt;br /&gt;
  display: inline;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  /* Sized in em so it keeps pace with A- / A+ rather than staying a fixed&lt;br /&gt;
     15px box against text that grows; clamped so it stays a sensible&lt;br /&gt;
     target at both extremes.&lt;br /&gt;
     Alignment was measured rather than guessed. vertical-align:middle is&lt;br /&gt;
     the only value that stays consistent as the type size changes (it is&lt;br /&gt;
     computed from the font&#039;s x-height), but it sits about 2.5px low&lt;br /&gt;
     against this line-height at every size - so that constant bias is&lt;br /&gt;
     taken back with `top`, giving under a pixel of error from 16px to&lt;br /&gt;
     30px. The element is already position:relative for its tick. */&lt;br /&gt;
  width: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  height: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  min-width: 13px; min-height: 13px;&lt;br /&gt;
  margin: 0 6px 0 0; padding: 0; cursor: pointer;&lt;br /&gt;
  vertical-align: middle; top: -2.5px;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  /* Placed and sized in % of the box, so the tick follows the em-based&lt;br /&gt;
     box instead of staying a 4x8px mark inside a checkbox that grew. */&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 32%; top: 8%;&lt;br /&gt;
  width: 26%; height: 55%;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The annotation count badge was removed (it was clipped by the round&lt;br /&gt;
   toggle&#039;s own overflow). This keeps it hidden for anyone still served a&lt;br /&gt;
   cached copy of the old gadget. */&lt;br /&gt;
#gra-toggle-badge { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── LINK LANDING ───────────────────────────────────────────────────&lt;br /&gt;
   Where a copied id or a bookmark citation link lands. The flash used to&lt;br /&gt;
   be injected by copyId.js; it lives here so the landing still reads when&lt;br /&gt;
   only common.js is loaded. */&lt;br /&gt;
.gid-target {&lt;br /&gt;
  animation: gid-target-flash 2.4s ease-out 1;&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
}&lt;br /&gt;
@keyframes gid-target-flash {&lt;br /&gt;
  0%, 55% { background: rgba(181,69,27,0.14); box-shadow: 0 0 0 4px rgba(181,69,27,0.10); }&lt;br /&gt;
  100%    { background: transparent; box-shadow: none; }&lt;br /&gt;
}&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .gid-target { animation: none; background: rgba(181,69,27,0.14); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── BOOKMARK CITATION ──────────────────────────────────────────────&lt;br /&gt;
   The reference a bookmark produces, shown on its card and copyable in&lt;br /&gt;
   one click. Set small and in the UI face deliberately: it is apparatus&lt;br /&gt;
   for the reader to take away, not part of the text. */&lt;br /&gt;
.gra-bookmark-cite {&lt;br /&gt;
  margin-top: 6px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.54), 15px);&lt;br /&gt;
  line-height: 1.5; color: #6b5b52;&lt;br /&gt;
  /* Ragged right and flush left. The hanging indent that was here reads&lt;br /&gt;
     correctly in a printed reference LIST, where it separates one entry&lt;br /&gt;
     from the next; on a single card it just looked like the lines had&lt;br /&gt;
     slipped. Justification came in from the panel and stretched the word&lt;br /&gt;
     gaps, which is what made the spacing look broken. */&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  text-indent: 0;&lt;br /&gt;
  padding-left: 0;&lt;br /&gt;
  overflow-wrap: break-word;&lt;br /&gt;
  word-break: normal;&lt;br /&gt;
  hyphens: none;&lt;br /&gt;
}&lt;br /&gt;
.gra-bookmark-cite a {&lt;br /&gt;
  color: inherit; text-decoration: none; border-bottom: 1px dotted #c3b3a9;&lt;br /&gt;
  overflow-wrap: anywhere;          /* only the URL may break mid-token */&lt;br /&gt;
}&lt;br /&gt;
.gra-bookmark-cite a:hover { color: #b5451b; border-bottom-color: #b5451b; }&lt;br /&gt;
.gra-bookmark-cite i { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
/* Where the bookmark sits in the text. A caption, not part of the&lt;br /&gt;
   citation - so it is set in the UI face and kept visually apart from&lt;br /&gt;
   the reference below it. */&lt;br /&gt;
.gra-bookmark-locator {&lt;br /&gt;
  margin-top: 5px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(10px, calc(var(--gr-font-size, 22px) * 0.48), 13px);&lt;br /&gt;
  font-weight: 600;&lt;br /&gt;
  letter-spacing: .02em;&lt;br /&gt;
  color: #8a7a70;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gra-cite-actions { display: flex; flex-wrap: wrap; gap: 6px; }&lt;br /&gt;
&lt;br /&gt;
.gra-cite-copy {&lt;br /&gt;
  margin-top: 6px; padding: 3px 10px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  background: #fff5f2; border: 1px solid #f0cfc4; border-radius: 20px;&lt;br /&gt;
  cursor: pointer; transition: background .15s, color .15s, border-color .15s;&lt;br /&gt;
}&lt;br /&gt;
.gra-cite-copy:hover { background: #b5451b; color: #fff; border-color: #b5451b; }&lt;br /&gt;
.gra-cite-copy--done { background: #2e6e40; color: #fff; border-color: #2e6e40; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7599</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7599"/>
		<updated>2026-09-30T11:59:04Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  /* CHANGED from inline-flex + vertical-align:middle. An inline-flex box&lt;br /&gt;
     aligned &amp;quot;middle&amp;quot; is positioned by its own height against the parent&#039;s&lt;br /&gt;
     x-height, so the whole control - checkbox AND label together - sat&lt;br /&gt;
     progressively lower than the text beside it as the reader enlarged the&lt;br /&gt;
     type (measured: 1.8px out at 16px, 3.6px at 30px). Left as plain&lt;br /&gt;
     inline, the label is ordinary text and lines up with its neighbours by&lt;br /&gt;
     definition; only the checkbox needs aligning, and it does that with an&lt;br /&gt;
     em-based offset that holds at any size. */&lt;br /&gt;
  display: inline;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  /* Sized in em so it keeps pace with A- / A+ rather than staying a fixed&lt;br /&gt;
     15px box against text that grows; clamped so it stays a sensible&lt;br /&gt;
     target at both extremes.&lt;br /&gt;
     Alignment was measured rather than guessed. vertical-align:middle is&lt;br /&gt;
     the only value that stays consistent as the type size changes (it is&lt;br /&gt;
     computed from the font&#039;s x-height), but it sits about 2.5px low&lt;br /&gt;
     against this line-height at every size - so that constant bias is&lt;br /&gt;
     taken back with `top`, giving under a pixel of error from 16px to&lt;br /&gt;
     30px. The element is already position:relative for its tick. */&lt;br /&gt;
  width: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  height: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  min-width: 13px; min-height: 13px;&lt;br /&gt;
  margin: 0 6px 0 0; padding: 0; cursor: pointer;&lt;br /&gt;
  vertical-align: middle; top: -2.5px;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  /* Placed and sized in % of the box, so the tick follows the em-based&lt;br /&gt;
     box instead of staying a 4x8px mark inside a checkbox that grew. */&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 32%; top: 8%;&lt;br /&gt;
  width: 26%; height: 55%;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The annotation count badge was removed (it was clipped by the round&lt;br /&gt;
   toggle&#039;s own overflow). This keeps it hidden for anyone still served a&lt;br /&gt;
   cached copy of the old gadget. */&lt;br /&gt;
#gra-toggle-badge { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── LINK LANDING ───────────────────────────────────────────────────&lt;br /&gt;
   Where a copied id or a bookmark citation link lands. The flash used to&lt;br /&gt;
   be injected by copyId.js; it lives here so the landing still reads when&lt;br /&gt;
   only common.js is loaded. */&lt;br /&gt;
.gid-target {&lt;br /&gt;
  animation: gid-target-flash 2.4s ease-out 1;&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
}&lt;br /&gt;
@keyframes gid-target-flash {&lt;br /&gt;
  0%, 55% { background: rgba(181,69,27,0.14); box-shadow: 0 0 0 4px rgba(181,69,27,0.10); }&lt;br /&gt;
  100%    { background: transparent; box-shadow: none; }&lt;br /&gt;
}&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .gid-target { animation: none; background: rgba(181,69,27,0.14); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── BOOKMARK CITATION ──────────────────────────────────────────────&lt;br /&gt;
   The reference a bookmark produces, shown on its card and copyable in&lt;br /&gt;
   one click. Set small and in the UI face deliberately: it is apparatus&lt;br /&gt;
   for the reader to take away, not part of the text. */&lt;br /&gt;
.gra-bookmark-cite {&lt;br /&gt;
  margin-top: 6px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.54), 15px);&lt;br /&gt;
  line-height: 1.45; color: #6b5b52;&lt;br /&gt;
  overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
/* The reference hangs its continuation lines, the way a reference list&lt;br /&gt;
   does on paper. The link inside it stays on the citation&#039;s own colour&lt;br /&gt;
   until hovered - it is a citation, not a navigation control. */&lt;br /&gt;
.gra-bookmark-cite { padding-left: 1.4em; text-indent: -1.4em; }&lt;br /&gt;
.gra-bookmark-cite a { color: inherit; text-decoration: none; border-bottom: 1px dotted #c3b3a9; }&lt;br /&gt;
.gra-bookmark-cite a:hover { color: #b5451b; border-bottom-color: #b5451b; }&lt;br /&gt;
.gra-bookmark-cite i { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
.gra-bookmark-intext {&lt;br /&gt;
  margin-top: 4px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(10px, calc(var(--gr-font-size, 22px) * 0.48), 13px);&lt;br /&gt;
  color: #8a7a70;&lt;br /&gt;
  overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gra-cite-locator { color: #a08b7e; }&lt;br /&gt;
&lt;br /&gt;
/* Where the bookmark sits in the text. A caption, not part of the&lt;br /&gt;
   citation - so it is set in the UI face and kept visually apart from&lt;br /&gt;
   the reference below it. */&lt;br /&gt;
.gra-bookmark-locator {&lt;br /&gt;
  margin-top: 5px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(10px, calc(var(--gr-font-size, 22px) * 0.48), 13px);&lt;br /&gt;
  font-weight: 600;&lt;br /&gt;
  letter-spacing: .02em;&lt;br /&gt;
  color: #8a7a70;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gra-cite-actions { display: flex; flex-wrap: wrap; gap: 6px; }&lt;br /&gt;
&lt;br /&gt;
.gra-cite-copy {&lt;br /&gt;
  margin-top: 6px; padding: 3px 10px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  background: #fff5f2; border: 1px solid #f0cfc4; border-radius: 20px;&lt;br /&gt;
  cursor: pointer; transition: background .15s, color .15s, border-color .15s;&lt;br /&gt;
}&lt;br /&gt;
.gra-cite-copy:hover { background: #b5451b; color: #fff; border-color: #b5451b; }&lt;br /&gt;
.gra-cite-copy--done { background: #2e6e40; color: #fff; border-color: #2e6e40; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7598</id>
		<title>MediaWiki:Gadget-GrAnnotations.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7598"/>
		<updated>2026-09-30T11:58:42Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── SHAREABLE CITATION ────────────────────────────────────────────&lt;br /&gt;
     A bookmark is personal, but what it produces is not: a reference in&lt;br /&gt;
     APA form plus a link that resolves for anyone.&lt;br /&gt;
&lt;br /&gt;
     The link cannot point at the bookmark itself. A bookmark wraps the&lt;br /&gt;
     reader&#039;s own selection in a span with an id generated in their&lt;br /&gt;
     browser; that span is never saved to the page, so #&amp;lt;bookmark-id&amp;gt;&lt;br /&gt;
     resolves for nobody else. The citation therefore anchors to the&lt;br /&gt;
     nearest enclosing block that the PUBLISHED page carries an id for -&lt;br /&gt;
     the same ids copyId.js links to, and the same ones cross-references&lt;br /&gt;
     across the site already use.&lt;br /&gt;
&lt;br /&gt;
     APA 7th, for a work on a website with no date:&lt;br /&gt;
       Author. (n.d.). Title (Locator). Site. Retrieved D Month Y, from URL&lt;br /&gt;
     n.d. because these works carry no publication date, and a retrieval&lt;br /&gt;
     date because a wiki page can change. The block reference is given as&lt;br /&gt;
     a parenthetical locator after the title, which is where APA puts a&lt;br /&gt;
     section or part designation. */&lt;br /&gt;
&lt;br /&gt;
  var CITE_BLOCK_SEL = &#039;.verse-block[id], .bhashyam-block[id], .teeka-block[id], &#039; +&lt;br /&gt;
                       &#039;[data-block-id][id], .gr-apparatus[id], .shloka[id]&#039;;&lt;br /&gt;
&lt;br /&gt;
  function citeAnchorFor(node) {&lt;br /&gt;
    var el = node &amp;amp;&amp;amp; node.nodeType === 3 ? node.parentNode : node;&lt;br /&gt;
    while (el &amp;amp;&amp;amp; el !== document.body) {&lt;br /&gt;
      if (el.matches &amp;amp;&amp;amp; el.matches(CITE_BLOCK_SEL)) return el;&lt;br /&gt;
      el = el.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Walks up from the cited block: an interleaved commentary block states&lt;br /&gt;
  // its own work, otherwise the page&#039;s title element does.&lt;br /&gt;
  // A reference written in English cites the work in IAST, so prefer the&lt;br /&gt;
  // transliterated forms the library supplies and keep Devanagari as the&lt;br /&gt;
  // fallback. common.js writes the library&#039;s values onto .gr-doc-title as&lt;br /&gt;
  // soon as they arrive, so reading the DOM here always gets the current&lt;br /&gt;
  // names without this module knowing the library exists.&lt;br /&gt;
  function citeSourceFor(el) {&lt;br /&gt;
    var n = el;&lt;br /&gt;
    while (n &amp;amp;&amp;amp; n !== document.body) {&lt;br /&gt;
      if (n.getAttribute &amp;amp;&amp;amp; n.getAttribute(&#039;data-cite-title&#039;)) {&lt;br /&gt;
        return {&lt;br /&gt;
          author: n.getAttribute(&#039;data-cite-author-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-author&#039;) || &#039;&#039;,&lt;br /&gt;
          title:  n.getAttribute(&#039;data-cite-title-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-title&#039;) || &#039;&#039;,&lt;br /&gt;
          page:   n.getAttribute(&#039;data-cite-page&#039;) || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      n = n.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return {&lt;br /&gt;
      author: (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-author-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-author&#039;))) || &#039;&#039;,&lt;br /&gt;
      title:  (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-doc-title-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-doc-title&#039;) ||&lt;br /&gt;
                     t.textContent.trim())) || document.title,&lt;br /&gt;
      page:   (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;,&lt;br /&gt;
      scheme: (t &amp;amp;&amp;amp; t.getAttribute(&#039;data-locator-scheme&#039;)) || &#039;&#039;&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Any block carrying a library id gets its author/title filled in from&lt;br /&gt;
  // the library, whether common.js injected it or the page was built with&lt;br /&gt;
  // it already in place. Doing it here rather than only at injection time&lt;br /&gt;
  // means a citation never depends on which path put the block on screen.&lt;br /&gt;
  function hydrateCitationSources(root) {&lt;br /&gt;
    if (!window.grLibrary) return;&lt;br /&gt;
    var nodes = (root || document).querySelectorAll(&#039;[data-cite-grantha-id]&#039;);&lt;br /&gt;
    Array.prototype.forEach.call(nodes, function (el) {&lt;br /&gt;
      if (el.getAttribute(&#039;data-cite-hydrated&#039;)) return;&lt;br /&gt;
      var gid = el.getAttribute(&#039;data-cite-grantha-id&#039;);&lt;br /&gt;
      if (!gid) return;&lt;br /&gt;
      el.setAttribute(&#039;data-cite-hydrated&#039;, &#039;1&#039;);&lt;br /&gt;
      window.grLibrary.lookup(gid).then(function (g) {&lt;br /&gt;
        if (!g) return;&lt;br /&gt;
        if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
        if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
        if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
        if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ── LOCATOR ─────────────────────────────────────────────────────────&lt;br /&gt;
  // &amp;quot;KKN_C01_S01_V03&amp;quot; is a build artefact, not a reference. A reader&lt;br /&gt;
  // citing it wants 1.1.3, and ideally to be told that those numbers are&lt;br /&gt;
  // adhyaya, valli and mantra. The page declares the division names it&lt;br /&gt;
  // knows (data-locator-scheme); where it declares none we still give the&lt;br /&gt;
  // dotted form, which is correct for every text - it is only the NAMES&lt;br /&gt;
  // of the divisions that vary from work to work.&lt;br /&gt;
  function parseScheme(s) {&lt;br /&gt;
    var out = {};&lt;br /&gt;
    (s || &#039;&#039;).split(&#039;,&#039;).forEach(function (pair) {&lt;br /&gt;
      var i = pair.indexOf(&#039;=&#039;);&lt;br /&gt;
      if (i &amp;gt; 0) out[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();&lt;br /&gt;
    });&lt;br /&gt;
    return out;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Segments that qualify the passage rather than number it: _B01 is the&lt;br /&gt;
  // first paragraph of the bhashya ON that verse, and a bare letter with&lt;br /&gt;
  // no number (Katha&#039;s &amp;quot;_I_&amp;quot; for an introduction) marks a position, not a&lt;br /&gt;
  // division. A NUMBERED segment is always part of the path - in&lt;br /&gt;
  // RG_C01_I01_V01 the I is a sukta, and reading it as Katha&#039;s&lt;br /&gt;
  // introduction marker turned 1.1.1 into &amp;quot;1.1, Sukta 1&amp;quot;.&lt;br /&gt;
  function isQualifier(k, n) { return k === &#039;B&#039; || !n; }&lt;br /&gt;
&lt;br /&gt;
  function parseLocator(id) {&lt;br /&gt;
    if (!id) return null;&lt;br /&gt;
    var segs = String(id).split(&#039;_&#039;);&lt;br /&gt;
    var path = [], qual = [];&lt;br /&gt;
    for (var i = 1; i &amp;lt; segs.length; i++) {&lt;br /&gt;
      var m = /^([A-Za-z]+)(\d*)$/.exec(segs[i]);&lt;br /&gt;
      if (!m) continue;&lt;br /&gt;
      var k = m[1].toUpperCase(), n = m[2] ? String(parseInt(m[2], 10)) : &#039;&#039;;&lt;br /&gt;
      (isQualifier(k, n) ? qual : path).push({ k: k, n: n });&lt;br /&gt;
    }&lt;br /&gt;
    if (!path.length &amp;amp;&amp;amp; !qual.length) return null;&lt;br /&gt;
    return { path: path, qual: qual };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function locatorText(id, scheme) {&lt;br /&gt;
    var p = parseLocator(id);&lt;br /&gt;
    if (!p) return { numeric: id || &#039;&#039;, spelled: &#039;&#039; };&lt;br /&gt;
    var labels = parseScheme(scheme);&lt;br /&gt;
    var numeric = p.path.map(function (s) { return s.n; }).filter(Boolean).join(&#039;.&#039;);&lt;br /&gt;
    var spelledParts = p.path.map(function (s) {&lt;br /&gt;
      return labels[s.k] ? labels[s.k] + &#039; &#039; + s.n : null;&lt;br /&gt;
    });&lt;br /&gt;
    var spelled = spelledParts.indexOf(null) === -1 ? spelledParts.join(&#039;, &#039;) : &#039;&#039;;&lt;br /&gt;
    p.qual.forEach(function (s) {&lt;br /&gt;
      var lab = labels[s.k] ||&lt;br /&gt;
                (s.k === &#039;B&#039; ? &#039;bhāṣya&#039; : (s.k === &#039;I&#039; ? &#039;intro&#039; : s.k));&lt;br /&gt;
      var bit = lab + (s.n ? &#039; &#039; + s.n : &#039;&#039;);&lt;br /&gt;
      numeric += (numeric ? &#039;, &#039; : &#039;&#039;) + bit;&lt;br /&gt;
      if (spelled) spelled += &#039;, &#039; + bit;&lt;br /&gt;
    });&lt;br /&gt;
    return { numeric: numeric || id, spelled: spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Version stamp, so it is possible to tell from the browser console&lt;br /&gt;
  // which build of this gadget a page is actually running. Check with&lt;br /&gt;
  //   window.grAnnotationsVersion&lt;br /&gt;
  // APA 7 asks for a retrieval date only when the content is designed to&lt;br /&gt;
  // change and is not archived. These are settled texts with a fixed&lt;br /&gt;
  // structure, so the date is omitted. Set true to restore it.&lt;br /&gt;
  var SHOW_RETRIEVAL_DATE = false;&lt;br /&gt;
&lt;br /&gt;
  window.grAnnotationsVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
&lt;br /&gt;
  // A citation is worthless if its link doesn&#039;t open, so prefer URLs we&lt;br /&gt;
  // KNOW are good over ones we assemble.&lt;br /&gt;
  //   - Same page as the reader: use the address they&#039;re already at. A wiki&lt;br /&gt;
  //     whose served paths differ from wgArticlePath (or that sits behind a&lt;br /&gt;
  //     rewrite) would otherwise yield a link that 404s.&lt;br /&gt;
  //   - Another page (an injected teeka block cites its own subpage): use&lt;br /&gt;
  //     mw.util.getUrl, the same builder every working link on the wiki&lt;br /&gt;
  //     comes from. wgArticlePath is only the last resort.&lt;br /&gt;
  function citeUrl(pageName, anchorId) {&lt;br /&gt;
    var here = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
    var frag = anchorId ? &#039;#&#039; + encodeURIComponent(anchorId) : &#039;&#039;;&lt;br /&gt;
    var base;&lt;br /&gt;
    if (!pageName || String(pageName) === String(here)) {&lt;br /&gt;
      base = location.origin + location.pathname;&lt;br /&gt;
    } else if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) {&lt;br /&gt;
      base = location.origin + mw.util.getUrl(String(pageName));&lt;br /&gt;
    } else {&lt;br /&gt;
      var path = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
      base = location.origin +&lt;br /&gt;
             path.replace(&#039;$1&#039;, String(pageName).split(&#039;/&#039;).map(encodeURIComponent).join(&#039;/&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    return base + frag;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var CITE_MONTHS = [&#039;January&#039;,&#039;February&#039;,&#039;March&#039;,&#039;April&#039;,&#039;May&#039;,&#039;June&#039;,&lt;br /&gt;
                     &#039;July&#039;,&#039;August&#039;,&#039;September&#039;,&#039;October&#039;,&#039;November&#039;,&#039;December&#039;];&lt;br /&gt;
&lt;br /&gt;
  function retrievedToday() {&lt;br /&gt;
    var d = new Date();&lt;br /&gt;
    return CITE_MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function siteName() {&lt;br /&gt;
    return (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgSiteName&#039;)) || location.hostname;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // APA 7th, webpage on a website (§10.16):&lt;br /&gt;
  //     Author. (n.d.). Title of work. Site Name. Retrieved &amp;lt;date&amp;gt;, from &amp;lt;URL&amp;gt;&lt;br /&gt;
  // Two rules this has to honour and an earlier draft did not:&lt;br /&gt;
  //   1. No known author -&amp;gt; the TITLE moves into the author position; you&lt;br /&gt;
  //      never leave a bare &amp;quot;(n.d.)&amp;quot; with nothing in front of it. And when&lt;br /&gt;
  //      the title occupies that slot the site name is not repeated after it.&lt;br /&gt;
  //   2. A locator (our block id) does NOT belong in the reference entry.&lt;br /&gt;
  //      The parenthetical slot after a title is for edition/format&lt;br /&gt;
  //      descriptors, not section numbers. APA puts locators in the in-text&lt;br /&gt;
  //      citation, so the block id goes there: (Author, n.d., IS_C01_I03).&lt;br /&gt;
  // The retrieval date is correct here because the page is designed to&lt;br /&gt;
  // change and we link to the live version rather than a permalink.&lt;br /&gt;
  // Titles of standalone works are italicised, which only the HTML flavour&lt;br /&gt;
  // of the clipboard can carry.&lt;br /&gt;
  // The library records names with their honorifics and, often, a&lt;br /&gt;
  // parenthetical alias: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;. A reference&lt;br /&gt;
  // entry carries the name a reader would look up, so the honorific and&lt;br /&gt;
  // the alias come off - leaving &amp;quot;Madhvācārya&amp;quot;. Only applied to the&lt;br /&gt;
  // romanised form; the Devanagari is left exactly as the library has it,&lt;br /&gt;
  // because there the honorific is fused to the name by sandhi&lt;br /&gt;
  // (श्रीमत् + आनन्दतीर्थः -&amp;gt; श्रीमदानन्दतीर्थः) and cannot be sliced off safely.&lt;br /&gt;
  var HONORIFIC_RE = /^(?:\u015br\u012b|\u015br\u012bman|\u015br\u012bmat|\u015br\u012bmad|sri|shri|sree)\s+/i;&lt;br /&gt;
  function citationAuthor(name) {&lt;br /&gt;
    var n = (name || &#039;&#039;).trim();&lt;br /&gt;
    if (!/[A-Za-z]/.test(n)) return n;            // Devanagari - leave alone&lt;br /&gt;
    n = n.replace(/\s*\([^)]*\)\s*$/, &#039;&#039;).trim();&lt;br /&gt;
    for (var i = 0; i &amp;lt; 3 &amp;amp;&amp;amp; HONORIFIC_RE.test(n); i++) {&lt;br /&gt;
      n = n.replace(HONORIFIC_RE, &#039;&#039;).trim();&lt;br /&gt;
    }&lt;br /&gt;
    return n || (name || &#039;&#039;).trim();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function buildCitation(rec) {&lt;br /&gt;
    var src    = rec.citeSource || {};&lt;br /&gt;
    var author = citationAuthor(src.author);&lt;br /&gt;
    var title  = (src.title  || &#039;&#039;).trim();&lt;br /&gt;
    var anchor = rec.anchor || &#039;&#039;;&lt;br /&gt;
    var site   = (siteName() || &#039;&#039;).trim();&lt;br /&gt;
    var url    = citeUrl(src.page, anchor);&lt;br /&gt;
    var retrieved = retrievedToday();&lt;br /&gt;
    var loc    = locatorText(anchor, src.scheme);&lt;br /&gt;
&lt;br /&gt;
    var head, headHtml, showSite;&lt;br /&gt;
    if (author) {&lt;br /&gt;
      head = author + &#039;. (n.d.). &#039; + title + &#039;.&#039;;&lt;br /&gt;
      headHtml = esc(author) + &#039;. (n.d.). &amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;.&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== author;&lt;br /&gt;
    } else {&lt;br /&gt;
      // Title in the author position - italicised there too, and the site&lt;br /&gt;
      // name is dropped when it would only repeat what the title says.&lt;br /&gt;
      head = title + &#039;. (n.d.).&#039;;&lt;br /&gt;
      headHtml = &#039;&amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;. (n.d.).&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== title;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // APA 7 (section 9.16) asks for a retrieval date only when the content&lt;br /&gt;
    // is designed to change and is not archived. A wiki page can change, so&lt;br /&gt;
    // the date is defensible - but these are settled texts, and a reader may&lt;br /&gt;
    // reasonably prefer the shorter form. Set SHOW_RETRIEVAL_DATE to false&lt;br /&gt;
    // for: Author. (n.d.). Title. Site. URL&lt;br /&gt;
    var tail = SHOW_RETRIEVAL_DATE&lt;br /&gt;
        ? &#039; Retrieved &#039; + retrieved + &#039;, from &#039; + url&lt;br /&gt;
        : &#039; &#039; + url;&lt;br /&gt;
    var reference = head + (showSite ? &#039; &#039; + site + &#039;.&#039; : &#039;&#039;) + tail;&lt;br /&gt;
    var referenceHtml = headHtml + (showSite ? &#039; &#039; + esc(site) + &#039;.&#039; : &#039;&#039;) + &#039; &#039;&lt;br /&gt;
        + (SHOW_RETRIEVAL_DATE ? &#039;Retrieved &#039; + esc(retrieved) + &#039;, from &#039; : &#039;&#039;)&lt;br /&gt;
        + &#039;&amp;lt;a href=&amp;quot;&#039; + esc(url) + &#039;&amp;quot;&amp;gt;&#039; + esc(url) + &#039;&amp;lt;/a&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
    // The in-text citation carries the locator, expanded: 1.1.3, not&lt;br /&gt;
    // KKN_C01_S01_V03. The name is shortened the way an in-text citation&lt;br /&gt;
    // shortens one - the library&#039;s parenthetical alias (&amp;quot;Śrīman&lt;br /&gt;
    // Madhvācārya (Ānandatīrtha)&amp;quot;) belongs in the reference, not here.&lt;br /&gt;
    var shortName = (author || title).replace(/\s*\([^)]*\)\s*$/, &#039;&#039;).trim();&lt;br /&gt;
    var inText = &#039;(&#039; + (shortName || author || title) + &#039;, n.d.&#039;&lt;br /&gt;
               + (loc.numeric ? &#039;, &#039; + loc.numeric : &#039;&#039;) + &#039;)&#039;;&lt;br /&gt;
&lt;br /&gt;
    return { reference: reference, referenceHtml: referenceHtml,&lt;br /&gt;
             inText: inText, url: url,&lt;br /&gt;
             locator: loc.numeric, locatorSpelled: loc.spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // html is optional. When given, both flavours go on the clipboard so a&lt;br /&gt;
  // paste into Word/Docs keeps the italicised title and the live link,&lt;br /&gt;
  // while a paste into a plain field (or the address bar) gets the text.&lt;br /&gt;
  function copyToClipboard(text, done, html) {&lt;br /&gt;
    if (html &amp;amp;&amp;amp; window.ClipboardItem &amp;amp;&amp;amp; navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.write) {&lt;br /&gt;
      try {&lt;br /&gt;
        var item = new ClipboardItem({&lt;br /&gt;
          &#039;text/html&#039;:  new Blob([html], { type: &#039;text/html&#039; }),&lt;br /&gt;
          &#039;text/plain&#039;: new Blob([text], { type: &#039;text/plain&#039; })&lt;br /&gt;
        });&lt;br /&gt;
        navigator.clipboard.write([item]).then(done).catch(function () {&lt;br /&gt;
          plainCopy(text, done);&lt;br /&gt;
        });&lt;br /&gt;
        return;&lt;br /&gt;
      } catch (e) { /* fall through */ }&lt;br /&gt;
    }&lt;br /&gt;
    plainCopy(text, done);&lt;br /&gt;
  }&lt;br /&gt;
  function plainCopy(text, done) {&lt;br /&gt;
    if (navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.writeText) {&lt;br /&gt;
      navigator.clipboard.writeText(text).then(done).catch(function () { legacyCopy(text, done); });&lt;br /&gt;
    } else { legacyCopy(text, done); }&lt;br /&gt;
  }&lt;br /&gt;
  function legacyCopy(text, done) {&lt;br /&gt;
    var ta = document.createElement(&#039;textarea&#039;);&lt;br /&gt;
    ta.value = text;&lt;br /&gt;
    ta.style.cssText = &#039;position:fixed;top:-9999px;left:-9999px;&#039;;&lt;br /&gt;
    document.body.appendChild(ta); ta.focus(); ta.select();&lt;br /&gt;
    try { document.execCommand(&#039;copy&#039;); done &amp;amp;&amp;amp; done(); } catch (e) {}&lt;br /&gt;
    document.body.removeChild(ta);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    // Resolve the citation anchor BEFORE wrapping: wrapSelection inserts a&lt;br /&gt;
    // span of its own, and resolving afterwards would just find that.&lt;br /&gt;
    var anchorEl = citeAnchorFor(_selRange &amp;amp;&amp;amp; _selRange.startContainer);&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    if (!anchorEl &amp;amp;&amp;amp; span) anchorEl = citeAnchorFor(span);&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso(),&lt;br /&gt;
               anchor: anchorEl ? (anchorEl.id || anchorEl.getAttribute(&#039;data-block-id&#039;) || &#039;&#039;) : &#039;&#039;,&lt;br /&gt;
               citeSource: citeSourceFor(anchorEl || document.body)};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      var cite = buildCitation(b);&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            // One button. The reference already carries the work, the&lt;br /&gt;
            // locator&#039;s page and the link, so a separate &amp;quot;copy link&amp;quot; only&lt;br /&gt;
            // asked the reader to decide something they don&#039;t need to.&lt;br /&gt;
            // The in-text form stays on the card because it is what goes&lt;br /&gt;
            // in a sentence, but it copies on click rather than earning a&lt;br /&gt;
            // button of its own.&lt;br /&gt;
            // Where in the text this sits, spelled out - &amp;quot;Adhyaya 3,&lt;br /&gt;
            // Mantra 4&amp;quot; rather than MUN_C03_V04. Not part of the citation&lt;br /&gt;
            // (APA keeps a locator out of the reference entry), so it is&lt;br /&gt;
            // set apart as a caption and is not included in the copy.&lt;br /&gt;
            + (cite.locatorSpelled || cite.locator&lt;br /&gt;
                 ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-locator&amp;quot;&amp;gt;&#039;&lt;br /&gt;
                     + esc(cite.locatorSpelled || cite.locator) + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                 : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-cite&amp;quot;&amp;gt;&#039;+cite.referenceHtml+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-cite-actions&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-what=&amp;quot;reference&amp;quot;&#039;&lt;br /&gt;
            + &#039; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;Copy citation&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    // CHANGED: grouped by source. A moola page now also carries the notes&lt;br /&gt;
    // of every commentary shown on it, and flattening them into one list&lt;br /&gt;
    // put a commentary&#039;s notes after the moola&#039;s with nothing saying which&lt;br /&gt;
    // was which - the cards were indistinguishable. Each note is filed&lt;br /&gt;
    // under the heading of the document it came from: the page&#039;s own notes&lt;br /&gt;
    // first, then one section per commentary, in the order the page shows&lt;br /&gt;
    // them. common.js wraps injected notes in .gr-teeka-notes-group with a&lt;br /&gt;
    // .gr-teeka-notes-title, which is where the heading comes from.&lt;br /&gt;
    function groupOf(li) {&lt;br /&gt;
      var wrap = li.closest ? li.closest(&#039;.gr-teeka-notes-group&#039;) : null;&lt;br /&gt;
      if (!wrap) return null;&lt;br /&gt;
      var t = wrap.querySelector(&#039;.gr-teeka-notes-title&#039;);&lt;br /&gt;
      return {&lt;br /&gt;
        slug: wrap.getAttribute(&#039;data-teeka-slug&#039;) || &#039;&#039;,&lt;br /&gt;
        title: t ? t.textContent.trim() : &#039;&#039;&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var sections = [];          // [{key, title, items: []}] in document order&lt;br /&gt;
    var byKey = {};&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var g = groupOf(li);&lt;br /&gt;
      var key = g ? g.slug : &#039;&#039;;&lt;br /&gt;
      if (!byKey[key]) {&lt;br /&gt;
        byKey[key] = { key: key, title: g ? g.title : &#039;&#039;, items: [] };&lt;br /&gt;
        sections.push(byKey[key]);&lt;br /&gt;
      }&lt;br /&gt;
      byKey[key].items.push(li);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    sections.forEach(function (sec) {&lt;br /&gt;
      if (sec.title) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title&amp;quot;&amp;gt;&#039; + esc(sec.title) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      } else if (sections.length &amp;gt; 1) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title gra-footnote-section-own&amp;quot;&amp;gt;&#039;&lt;br /&gt;
              + esc(pageOwnNotesLabel()) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      }&lt;br /&gt;
      sec.items.forEach(function (li) { html += footnoteCard(li, sec.title); });&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The heading for the page&#039;s own notes, shown only when there is at&lt;br /&gt;
  // least one commentary section to tell them apart from.&lt;br /&gt;
  function pageOwnNotesLabel() {&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return (t &amp;amp;&amp;amp; t.textContent.trim()) || &#039;This page&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function footnoteCard(li, groupTitle) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      // The commentary&#039;s name is repeated on the card itself, not only on&lt;br /&gt;
      // the section heading: a reader who has scrolled the panel, or who&lt;br /&gt;
      // arrives at a card by clicking a word in the text, sees the card&lt;br /&gt;
      // without its heading in view.&lt;br /&gt;
      return &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (groupTitle ? &#039;&amp;lt;div class=&amp;quot;gra-footnote-source&amp;quot;&amp;gt;&#039;+esc(groupTitle)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-cite-copy&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-cite-copy&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $btn = $(this), id = $btn.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      var cite = buildCitation(rec);&lt;br /&gt;
      var what = $btn.attr(&#039;data-cite-what&#039;) || &#039;reference&#039;;&lt;br /&gt;
      // Only the reference carries formatting worth preserving; the in-text&lt;br /&gt;
      // form and the bare link must stay plain so they survive a paste into&lt;br /&gt;
      // the address bar.&lt;br /&gt;
      var text = cite[what] || cite.reference;&lt;br /&gt;
      var html = (what === &#039;reference&#039;) ? cite.referenceHtml : null;&lt;br /&gt;
      var was = $btn.text();&lt;br /&gt;
      copyToClipboard(text, function(){&lt;br /&gt;
        $btn.text(&#039;Copied&#039;).addClass(&#039;gra-cite-copy--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $btn.text(was).removeClass(&#039;gra-cite-copy--done&#039;); }, 1600);&lt;br /&gt;
      }, html);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
    hydrateCitationSources();&lt;br /&gt;
    // Commentary blocks arrive after the page does, so hydrate again when&lt;br /&gt;
    // they land rather than only once at boot.&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
      hydrateCitationSources(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  // The count badge is gone. It sat on the round toggle, which clips its&lt;br /&gt;
  // own overflow to stay circular, so the badge was cut off on every side&lt;br /&gt;
  // and told the reader nothing they could act on. Kept as a no-op so the&lt;br /&gt;
  // five call sites that report a change still read clearly.&lt;br /&gt;
  function updateBadge() { }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7597</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7597"/>
		<updated>2026-09-30T11:40:24Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont, limit) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: limit, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                // The API answers 200 with an {error:...} body, so a failed&lt;br /&gt;
                // query is not a rejected promise. Treat it as one.&lt;br /&gt;
                if (j &amp;amp;&amp;amp; j.error) throw new Error(j.error.code || &#039;api error&#039;);&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) {&lt;br /&gt;
                    return page(j[&#039;continue&#039;].rccontinue, limit);&lt;br /&gt;
                }&lt;br /&gt;
                return all;&lt;br /&gt;
            }).catch(function (err) {&lt;br /&gt;
                // recentchanges over a date range is the most expensive query&lt;br /&gt;
                // this site makes, and on a busy or constrained database it&lt;br /&gt;
                // can come back as a DBQueryError. Halve the window and retry&lt;br /&gt;
                // once; failing that, return nothing. A &amp;quot;what&#039;s new&amp;quot; list is&lt;br /&gt;
                // not worth breaking the page for.&lt;br /&gt;
                if (limit &amp;gt; 100) return page(cont, 100);&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[gr] recent changes unavailable: &#039; +&lt;br /&gt;
                                 (err &amp;amp;&amp;amp; err.message || err));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null, 500);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([&lt;br /&gt;
            fetchChanges().catch(function () { return []; }),&lt;br /&gt;
            fetchCatalogue().catch(function () { return null; })&lt;br /&gt;
        ]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // ---- CONFIGURE ME -------------------------------------------------&lt;br /&gt;
    // Where the Grantha library&#039;s REST API lives. Same-origin by default,&lt;br /&gt;
    // which needs no CORS - but the library is its own app, so unless it is&lt;br /&gt;
    // reverse-proxied under the wiki&#039;s domain this must be its real host:&lt;br /&gt;
    //     var API_BASE = &#039;https://library.example.org/api/granthas&#039;;&lt;br /&gt;
    // On another host the library must send&lt;br /&gt;
    //     Access-Control-Allow-Origin: https://anandamakaranda.in&lt;br /&gt;
    // or the browser will refuse the reply and every citation will fall&lt;br /&gt;
    // back to the author baked into the page (usually none).&lt;br /&gt;
    var API_BASE = window.GR_LIBRARY_API || &#039;/api/granthas&#039;;&lt;br /&gt;
    // Set to false while the library is not yet reachable: no request is&lt;br /&gt;
    // made at all, and citations use whatever the page itself carries.&lt;br /&gt;
    // Flip it back to true once API_BASE resolves.&lt;br /&gt;
    var LIBRARY_ENABLED = true;&lt;br /&gt;
    // -------------------------------------------------------------------&lt;br /&gt;
    var API = API_BASE.replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var mem = {};&lt;br /&gt;
    var warned = false;&lt;br /&gt;
    // Once the library has answered &amp;quot;not here&amp;quot;, every further call will get&lt;br /&gt;
    // the same answer. Stop asking: a wrong or missing API base otherwise&lt;br /&gt;
    // fills the network log with one 404 per work on the page.&lt;br /&gt;
    var libraryDown = !LIBRARY_ENABLED;&lt;br /&gt;
&lt;br /&gt;
    // In memory only, for the life of the page. Names used to be kept in&lt;br /&gt;
    // sessionStorage for six hours, which quietly outlived any correction&lt;br /&gt;
    // made in the library or on the map page. They now come from the map&lt;br /&gt;
    // page, which is read once per load, so there is nothing to gain from&lt;br /&gt;
    // a second cache that can disagree with it.&lt;br /&gt;
    function cacheGet(id) { return mem[id] || null; }&lt;br /&gt;
    function cacheSet(id, v) { mem[id] = v; }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        // The map page carries the names for every work this wiki uses, so&lt;br /&gt;
        // normally nothing is fetched from the library at all. That matters&lt;br /&gt;
        // because the library is someone else&#039;s server: we cannot add CORS&lt;br /&gt;
        // headers to it, and a cross-origin call we cannot configure is a&lt;br /&gt;
        // call that will not work. The live API stays as an enhancement for&lt;br /&gt;
        // whenever it IS reachable.&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var w = (m.works || {})[id];&lt;br /&gt;
            if (w &amp;amp;&amp;amp; (w.name || w.nameIAST)) {&lt;br /&gt;
                var v = { id: id, name: w.name || &#039;&#039;, nameIAST: w.nameIAST || &#039;&#039;,&lt;br /&gt;
                          author: w.author || &#039;&#039;, authorIAST: w.authorIAST || &#039;&#039;,&lt;br /&gt;
                          category: w.category || &#039;&#039;, prasthana: w.prasthana || &#039;&#039; };&lt;br /&gt;
                cacheSet(id, v);&lt;br /&gt;
                return v;&lt;br /&gt;
            }&lt;br /&gt;
            return fetchFromLibrary(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchFromLibrary(id) {&lt;br /&gt;
        if (libraryDown) return Promise.resolve(null);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            // A 404 or 5xx means the base URL is wrong or the service is&lt;br /&gt;
            // down - not that this one work is missing - so trip the breaker.&lt;br /&gt;
            if (!r.ok) {&lt;br /&gt;
                if (r.status === 404 || r.status &amp;gt;= 500) libraryDown = true;&lt;br /&gt;
                throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            }&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function (err) {&lt;br /&gt;
            // Offline, blocked by CORS, wrong host, or the id is not in the&lt;br /&gt;
            // library yet. The page&#039;s own data-author/data-doc-title stand&lt;br /&gt;
            // in, so a citation is never blocked on this request - but an&lt;br /&gt;
            // unreachable library looks exactly like a work with no author,&lt;br /&gt;
            // so say which it is, once.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            libraryDown = true;&lt;br /&gt;
            if (!warned) {&lt;br /&gt;
                warned = true;&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[grantha] could not read the library at &#039; + API +&lt;br /&gt;
                        &#039; (asked for &amp;quot;&#039; + id + &#039;&amp;quot;): &#039; + (err &amp;amp;&amp;amp; err.message || err) +&lt;br /&gt;
                        &#039;. Citations will use whatever the page itself carries. &#039; +&lt;br /&gt;
                        &#039;Set window.GR_LIBRARY_API to the library host, and allow &#039; +&lt;br /&gt;
                        &#039;this origin via Access-Control-Allow-Origin.&#039;);&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            }&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        // Deliberately NOT cached in sessionStorage. It was, for six hours,&lt;br /&gt;
        // which meant that editing the map page changed nothing for anyone&lt;br /&gt;
        // whose tab had already read the old one - the exact failure you hit&lt;br /&gt;
        // while setting this up. mapPromise already collapses it to one&lt;br /&gt;
        // request per page load, and the browser&#039;s own HTTP cache handles&lt;br /&gt;
        // the rest; a stale map is far more expensive than a small fetch.&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr-lib:map&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages, works,&lt;br /&gt;
                // locators} when names and division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m };&lt;br /&gt;
                m.works = m.works || {};&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try {&lt;br /&gt;
                    if (!Object.keys(m.works).length) {&lt;br /&gt;
                        console.info(&#039;[grantha] the map page has no &amp;quot;works&amp;quot; section, &#039; +&lt;br /&gt;
                            &#039;so names must come from the library API. Re-run &#039; +&lt;br /&gt;
                            &#039;sync_grantha_map.py to embed them.&#039;);&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (libraryDown) return Promise.resolve({});&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
              if (!r.ok) { libraryDown = true; return null; }&lt;br /&gt;
              return r.json();&lt;br /&gt;
          })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        // lookup() answers from the map page without a network call, so&lt;br /&gt;
        // resolving fifty works costs one shared map fetch, not fifty&lt;br /&gt;
        // requests. Only ids the map does not name reach the library.&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7596</id>
		<title>MediaWiki:Gadget-GrAnnotations.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7596"/>
		<updated>2026-09-30T11:32:13Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── SHAREABLE CITATION ────────────────────────────────────────────&lt;br /&gt;
     A bookmark is personal, but what it produces is not: a reference in&lt;br /&gt;
     APA form plus a link that resolves for anyone.&lt;br /&gt;
&lt;br /&gt;
     The link cannot point at the bookmark itself. A bookmark wraps the&lt;br /&gt;
     reader&#039;s own selection in a span with an id generated in their&lt;br /&gt;
     browser; that span is never saved to the page, so #&amp;lt;bookmark-id&amp;gt;&lt;br /&gt;
     resolves for nobody else. The citation therefore anchors to the&lt;br /&gt;
     nearest enclosing block that the PUBLISHED page carries an id for -&lt;br /&gt;
     the same ids copyId.js links to, and the same ones cross-references&lt;br /&gt;
     across the site already use.&lt;br /&gt;
&lt;br /&gt;
     APA 7th, for a work on a website with no date:&lt;br /&gt;
       Author. (n.d.). Title (Locator). Site. Retrieved D Month Y, from URL&lt;br /&gt;
     n.d. because these works carry no publication date, and a retrieval&lt;br /&gt;
     date because a wiki page can change. The block reference is given as&lt;br /&gt;
     a parenthetical locator after the title, which is where APA puts a&lt;br /&gt;
     section or part designation. */&lt;br /&gt;
&lt;br /&gt;
  var CITE_BLOCK_SEL = &#039;.verse-block[id], .bhashyam-block[id], .teeka-block[id], &#039; +&lt;br /&gt;
                       &#039;[data-block-id][id], .gr-apparatus[id], .shloka[id]&#039;;&lt;br /&gt;
&lt;br /&gt;
  function citeAnchorFor(node) {&lt;br /&gt;
    var el = node &amp;amp;&amp;amp; node.nodeType === 3 ? node.parentNode : node;&lt;br /&gt;
    while (el &amp;amp;&amp;amp; el !== document.body) {&lt;br /&gt;
      if (el.matches &amp;amp;&amp;amp; el.matches(CITE_BLOCK_SEL)) return el;&lt;br /&gt;
      el = el.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Walks up from the cited block: an interleaved commentary block states&lt;br /&gt;
  // its own work, otherwise the page&#039;s title element does.&lt;br /&gt;
  // A reference written in English cites the work in IAST, so prefer the&lt;br /&gt;
  // transliterated forms the library supplies and keep Devanagari as the&lt;br /&gt;
  // fallback. common.js writes the library&#039;s values onto .gr-doc-title as&lt;br /&gt;
  // soon as they arrive, so reading the DOM here always gets the current&lt;br /&gt;
  // names without this module knowing the library exists.&lt;br /&gt;
  function citeSourceFor(el) {&lt;br /&gt;
    var n = el;&lt;br /&gt;
    while (n &amp;amp;&amp;amp; n !== document.body) {&lt;br /&gt;
      if (n.getAttribute &amp;amp;&amp;amp; n.getAttribute(&#039;data-cite-title&#039;)) {&lt;br /&gt;
        return {&lt;br /&gt;
          author: n.getAttribute(&#039;data-cite-author-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-author&#039;) || &#039;&#039;,&lt;br /&gt;
          title:  n.getAttribute(&#039;data-cite-title-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-title&#039;) || &#039;&#039;,&lt;br /&gt;
          page:   n.getAttribute(&#039;data-cite-page&#039;) || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      n = n.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return {&lt;br /&gt;
      author: (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-author-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-author&#039;))) || &#039;&#039;,&lt;br /&gt;
      title:  (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-doc-title-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-doc-title&#039;) ||&lt;br /&gt;
                     t.textContent.trim())) || document.title,&lt;br /&gt;
      page:   (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;,&lt;br /&gt;
      scheme: (t &amp;amp;&amp;amp; t.getAttribute(&#039;data-locator-scheme&#039;)) || &#039;&#039;&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Any block carrying a library id gets its author/title filled in from&lt;br /&gt;
  // the library, whether common.js injected it or the page was built with&lt;br /&gt;
  // it already in place. Doing it here rather than only at injection time&lt;br /&gt;
  // means a citation never depends on which path put the block on screen.&lt;br /&gt;
  function hydrateCitationSources(root) {&lt;br /&gt;
    if (!window.grLibrary) return;&lt;br /&gt;
    var nodes = (root || document).querySelectorAll(&#039;[data-cite-grantha-id]&#039;);&lt;br /&gt;
    Array.prototype.forEach.call(nodes, function (el) {&lt;br /&gt;
      if (el.getAttribute(&#039;data-cite-hydrated&#039;)) return;&lt;br /&gt;
      var gid = el.getAttribute(&#039;data-cite-grantha-id&#039;);&lt;br /&gt;
      if (!gid) return;&lt;br /&gt;
      el.setAttribute(&#039;data-cite-hydrated&#039;, &#039;1&#039;);&lt;br /&gt;
      window.grLibrary.lookup(gid).then(function (g) {&lt;br /&gt;
        if (!g) return;&lt;br /&gt;
        if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
        if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
        if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
        if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ── LOCATOR ─────────────────────────────────────────────────────────&lt;br /&gt;
  // &amp;quot;KKN_C01_S01_V03&amp;quot; is a build artefact, not a reference. A reader&lt;br /&gt;
  // citing it wants 1.1.3, and ideally to be told that those numbers are&lt;br /&gt;
  // adhyaya, valli and mantra. The page declares the division names it&lt;br /&gt;
  // knows (data-locator-scheme); where it declares none we still give the&lt;br /&gt;
  // dotted form, which is correct for every text - it is only the NAMES&lt;br /&gt;
  // of the divisions that vary from work to work.&lt;br /&gt;
  function parseScheme(s) {&lt;br /&gt;
    var out = {};&lt;br /&gt;
    (s || &#039;&#039;).split(&#039;,&#039;).forEach(function (pair) {&lt;br /&gt;
      var i = pair.indexOf(&#039;=&#039;);&lt;br /&gt;
      if (i &amp;gt; 0) out[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();&lt;br /&gt;
    });&lt;br /&gt;
    return out;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Segments that qualify the passage rather than number it: _B01 is the&lt;br /&gt;
  // first paragraph of the bhashya ON that verse, and a bare letter with&lt;br /&gt;
  // no number (Katha&#039;s &amp;quot;_I_&amp;quot; for an introduction) marks a position, not a&lt;br /&gt;
  // division. A NUMBERED segment is always part of the path - in&lt;br /&gt;
  // RG_C01_I01_V01 the I is a sukta, and reading it as Katha&#039;s&lt;br /&gt;
  // introduction marker turned 1.1.1 into &amp;quot;1.1, Sukta 1&amp;quot;.&lt;br /&gt;
  function isQualifier(k, n) { return k === &#039;B&#039; || !n; }&lt;br /&gt;
&lt;br /&gt;
  function parseLocator(id) {&lt;br /&gt;
    if (!id) return null;&lt;br /&gt;
    var segs = String(id).split(&#039;_&#039;);&lt;br /&gt;
    var path = [], qual = [];&lt;br /&gt;
    for (var i = 1; i &amp;lt; segs.length; i++) {&lt;br /&gt;
      var m = /^([A-Za-z]+)(\d*)$/.exec(segs[i]);&lt;br /&gt;
      if (!m) continue;&lt;br /&gt;
      var k = m[1].toUpperCase(), n = m[2] ? String(parseInt(m[2], 10)) : &#039;&#039;;&lt;br /&gt;
      (isQualifier(k, n) ? qual : path).push({ k: k, n: n });&lt;br /&gt;
    }&lt;br /&gt;
    if (!path.length &amp;amp;&amp;amp; !qual.length) return null;&lt;br /&gt;
    return { path: path, qual: qual };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function locatorText(id, scheme) {&lt;br /&gt;
    var p = parseLocator(id);&lt;br /&gt;
    if (!p) return { numeric: id || &#039;&#039;, spelled: &#039;&#039; };&lt;br /&gt;
    var labels = parseScheme(scheme);&lt;br /&gt;
    var numeric = p.path.map(function (s) { return s.n; }).filter(Boolean).join(&#039;.&#039;);&lt;br /&gt;
    var spelledParts = p.path.map(function (s) {&lt;br /&gt;
      return labels[s.k] ? labels[s.k] + &#039; &#039; + s.n : null;&lt;br /&gt;
    });&lt;br /&gt;
    var spelled = spelledParts.indexOf(null) === -1 ? spelledParts.join(&#039;, &#039;) : &#039;&#039;;&lt;br /&gt;
    p.qual.forEach(function (s) {&lt;br /&gt;
      var lab = labels[s.k] ||&lt;br /&gt;
                (s.k === &#039;B&#039; ? &#039;bhāṣya&#039; : (s.k === &#039;I&#039; ? &#039;intro&#039; : s.k));&lt;br /&gt;
      var bit = lab + (s.n ? &#039; &#039; + s.n : &#039;&#039;);&lt;br /&gt;
      numeric += (numeric ? &#039;, &#039; : &#039;&#039;) + bit;&lt;br /&gt;
      if (spelled) spelled += &#039;, &#039; + bit;&lt;br /&gt;
    });&lt;br /&gt;
    return { numeric: numeric || id, spelled: spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Version stamp, so it is possible to tell from the browser console&lt;br /&gt;
  // which build of this gadget a page is actually running. Check with&lt;br /&gt;
  //   window.grAnnotationsVersion&lt;br /&gt;
  // Set false to drop &amp;quot;Retrieved &amp;lt;date&amp;gt;, from&amp;quot; and give just the URL.&lt;br /&gt;
  var SHOW_RETRIEVAL_DATE = true;&lt;br /&gt;
&lt;br /&gt;
  window.grAnnotationsVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
&lt;br /&gt;
  // A citation is worthless if its link doesn&#039;t open, so prefer URLs we&lt;br /&gt;
  // KNOW are good over ones we assemble.&lt;br /&gt;
  //   - Same page as the reader: use the address they&#039;re already at. A wiki&lt;br /&gt;
  //     whose served paths differ from wgArticlePath (or that sits behind a&lt;br /&gt;
  //     rewrite) would otherwise yield a link that 404s.&lt;br /&gt;
  //   - Another page (an injected teeka block cites its own subpage): use&lt;br /&gt;
  //     mw.util.getUrl, the same builder every working link on the wiki&lt;br /&gt;
  //     comes from. wgArticlePath is only the last resort.&lt;br /&gt;
  function citeUrl(pageName, anchorId) {&lt;br /&gt;
    var here = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
    var frag = anchorId ? &#039;#&#039; + encodeURIComponent(anchorId) : &#039;&#039;;&lt;br /&gt;
    var base;&lt;br /&gt;
    if (!pageName || String(pageName) === String(here)) {&lt;br /&gt;
      base = location.origin + location.pathname;&lt;br /&gt;
    } else if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) {&lt;br /&gt;
      base = location.origin + mw.util.getUrl(String(pageName));&lt;br /&gt;
    } else {&lt;br /&gt;
      var path = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
      base = location.origin +&lt;br /&gt;
             path.replace(&#039;$1&#039;, String(pageName).split(&#039;/&#039;).map(encodeURIComponent).join(&#039;/&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    return base + frag;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var CITE_MONTHS = [&#039;January&#039;,&#039;February&#039;,&#039;March&#039;,&#039;April&#039;,&#039;May&#039;,&#039;June&#039;,&lt;br /&gt;
                     &#039;July&#039;,&#039;August&#039;,&#039;September&#039;,&#039;October&#039;,&#039;November&#039;,&#039;December&#039;];&lt;br /&gt;
&lt;br /&gt;
  function retrievedToday() {&lt;br /&gt;
    var d = new Date();&lt;br /&gt;
    return CITE_MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function siteName() {&lt;br /&gt;
    return (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgSiteName&#039;)) || location.hostname;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // APA 7th, webpage on a website (§10.16):&lt;br /&gt;
  //     Author. (n.d.). Title of work. Site Name. Retrieved &amp;lt;date&amp;gt;, from &amp;lt;URL&amp;gt;&lt;br /&gt;
  // Two rules this has to honour and an earlier draft did not:&lt;br /&gt;
  //   1. No known author -&amp;gt; the TITLE moves into the author position; you&lt;br /&gt;
  //      never leave a bare &amp;quot;(n.d.)&amp;quot; with nothing in front of it. And when&lt;br /&gt;
  //      the title occupies that slot the site name is not repeated after it.&lt;br /&gt;
  //   2. A locator (our block id) does NOT belong in the reference entry.&lt;br /&gt;
  //      The parenthetical slot after a title is for edition/format&lt;br /&gt;
  //      descriptors, not section numbers. APA puts locators in the in-text&lt;br /&gt;
  //      citation, so the block id goes there: (Author, n.d., IS_C01_I03).&lt;br /&gt;
  // The retrieval date is correct here because the page is designed to&lt;br /&gt;
  // change and we link to the live version rather than a permalink.&lt;br /&gt;
  // Titles of standalone works are italicised, which only the HTML flavour&lt;br /&gt;
  // of the clipboard can carry.&lt;br /&gt;
  function buildCitation(rec) {&lt;br /&gt;
    var src    = rec.citeSource || {};&lt;br /&gt;
    var author = (src.author || &#039;&#039;).trim();&lt;br /&gt;
    var title  = (src.title  || &#039;&#039;).trim();&lt;br /&gt;
    var anchor = rec.anchor || &#039;&#039;;&lt;br /&gt;
    var site   = (siteName() || &#039;&#039;).trim();&lt;br /&gt;
    var url    = citeUrl(src.page, anchor);&lt;br /&gt;
    var retrieved = retrievedToday();&lt;br /&gt;
    var loc    = locatorText(anchor, src.scheme);&lt;br /&gt;
&lt;br /&gt;
    var head, headHtml, showSite;&lt;br /&gt;
    if (author) {&lt;br /&gt;
      head = author + &#039;. (n.d.). &#039; + title + &#039;.&#039;;&lt;br /&gt;
      headHtml = esc(author) + &#039;. (n.d.). &amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;.&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== author;&lt;br /&gt;
    } else {&lt;br /&gt;
      // Title in the author position - italicised there too, and the site&lt;br /&gt;
      // name is dropped when it would only repeat what the title says.&lt;br /&gt;
      head = title + &#039;. (n.d.).&#039;;&lt;br /&gt;
      headHtml = &#039;&amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;. (n.d.).&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== title;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // APA 7 (section 9.16) asks for a retrieval date only when the content&lt;br /&gt;
    // is designed to change and is not archived. A wiki page can change, so&lt;br /&gt;
    // the date is defensible - but these are settled texts, and a reader may&lt;br /&gt;
    // reasonably prefer the shorter form. Set SHOW_RETRIEVAL_DATE to false&lt;br /&gt;
    // for: Author. (n.d.). Title. Site. URL&lt;br /&gt;
    var tail = SHOW_RETRIEVAL_DATE&lt;br /&gt;
        ? &#039; Retrieved &#039; + retrieved + &#039;, from &#039; + url&lt;br /&gt;
        : &#039; &#039; + url;&lt;br /&gt;
    var reference = head + (showSite ? &#039; &#039; + site + &#039;.&#039; : &#039;&#039;) + tail;&lt;br /&gt;
    var referenceHtml = headHtml + (showSite ? &#039; &#039; + esc(site) + &#039;.&#039; : &#039;&#039;) + &#039; &#039;&lt;br /&gt;
        + (SHOW_RETRIEVAL_DATE ? &#039;Retrieved &#039; + esc(retrieved) + &#039;, from &#039; : &#039;&#039;)&lt;br /&gt;
        + &#039;&amp;lt;a href=&amp;quot;&#039; + esc(url) + &#039;&amp;quot;&amp;gt;&#039; + esc(url) + &#039;&amp;lt;/a&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
    // The in-text citation carries the locator, expanded: 1.1.3, not&lt;br /&gt;
    // KKN_C01_S01_V03. The name is shortened the way an in-text citation&lt;br /&gt;
    // shortens one - the library&#039;s parenthetical alias (&amp;quot;Śrīman&lt;br /&gt;
    // Madhvācārya (Ānandatīrtha)&amp;quot;) belongs in the reference, not here.&lt;br /&gt;
    var shortName = (author || title).replace(/\s*\([^)]*\)\s*$/, &#039;&#039;).trim();&lt;br /&gt;
    var inText = &#039;(&#039; + (shortName || author || title) + &#039;, n.d.&#039;&lt;br /&gt;
               + (loc.numeric ? &#039;, &#039; + loc.numeric : &#039;&#039;) + &#039;)&#039;;&lt;br /&gt;
&lt;br /&gt;
    return { reference: reference, referenceHtml: referenceHtml,&lt;br /&gt;
             inText: inText, url: url,&lt;br /&gt;
             locator: loc.numeric, locatorSpelled: loc.spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // html is optional. When given, both flavours go on the clipboard so a&lt;br /&gt;
  // paste into Word/Docs keeps the italicised title and the live link,&lt;br /&gt;
  // while a paste into a plain field (or the address bar) gets the text.&lt;br /&gt;
  function copyToClipboard(text, done, html) {&lt;br /&gt;
    if (html &amp;amp;&amp;amp; window.ClipboardItem &amp;amp;&amp;amp; navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.write) {&lt;br /&gt;
      try {&lt;br /&gt;
        var item = new ClipboardItem({&lt;br /&gt;
          &#039;text/html&#039;:  new Blob([html], { type: &#039;text/html&#039; }),&lt;br /&gt;
          &#039;text/plain&#039;: new Blob([text], { type: &#039;text/plain&#039; })&lt;br /&gt;
        });&lt;br /&gt;
        navigator.clipboard.write([item]).then(done).catch(function () {&lt;br /&gt;
          plainCopy(text, done);&lt;br /&gt;
        });&lt;br /&gt;
        return;&lt;br /&gt;
      } catch (e) { /* fall through */ }&lt;br /&gt;
    }&lt;br /&gt;
    plainCopy(text, done);&lt;br /&gt;
  }&lt;br /&gt;
  function plainCopy(text, done) {&lt;br /&gt;
    if (navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.writeText) {&lt;br /&gt;
      navigator.clipboard.writeText(text).then(done).catch(function () { legacyCopy(text, done); });&lt;br /&gt;
    } else { legacyCopy(text, done); }&lt;br /&gt;
  }&lt;br /&gt;
  function legacyCopy(text, done) {&lt;br /&gt;
    var ta = document.createElement(&#039;textarea&#039;);&lt;br /&gt;
    ta.value = text;&lt;br /&gt;
    ta.style.cssText = &#039;position:fixed;top:-9999px;left:-9999px;&#039;;&lt;br /&gt;
    document.body.appendChild(ta); ta.focus(); ta.select();&lt;br /&gt;
    try { document.execCommand(&#039;copy&#039;); done &amp;amp;&amp;amp; done(); } catch (e) {}&lt;br /&gt;
    document.body.removeChild(ta);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    // Resolve the citation anchor BEFORE wrapping: wrapSelection inserts a&lt;br /&gt;
    // span of its own, and resolving afterwards would just find that.&lt;br /&gt;
    var anchorEl = citeAnchorFor(_selRange &amp;amp;&amp;amp; _selRange.startContainer);&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    if (!anchorEl &amp;amp;&amp;amp; span) anchorEl = citeAnchorFor(span);&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso(),&lt;br /&gt;
               anchor: anchorEl ? (anchorEl.id || anchorEl.getAttribute(&#039;data-block-id&#039;) || &#039;&#039;) : &#039;&#039;,&lt;br /&gt;
               citeSource: citeSourceFor(anchorEl || document.body)};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      var cite = buildCitation(b);&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            // One button. The reference already carries the work, the&lt;br /&gt;
            // locator&#039;s page and the link, so a separate &amp;quot;copy link&amp;quot; only&lt;br /&gt;
            // asked the reader to decide something they don&#039;t need to.&lt;br /&gt;
            // The in-text form stays on the card because it is what goes&lt;br /&gt;
            // in a sentence, but it copies on click rather than earning a&lt;br /&gt;
            // button of its own.&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-cite&amp;quot;&amp;gt;&#039;+cite.referenceHtml+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-intext&amp;quot; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&#039;&lt;br /&gt;
            + &#039; title=&amp;quot;Click to copy the in-text citation&amp;quot;&amp;gt;In text: &#039;+esc(cite.inText)&lt;br /&gt;
            + (cite.locatorSpelled ? &#039; &amp;lt;span class=&amp;quot;gra-cite-locator&amp;quot;&amp;gt;(&#039;&lt;br /&gt;
                 + esc(cite.locatorSpelled) + &#039;)&amp;lt;/span&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-cite-actions&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-what=&amp;quot;reference&amp;quot;&#039;&lt;br /&gt;
            + &#039; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;Copy citation&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    // CHANGED: grouped by source. A moola page now also carries the notes&lt;br /&gt;
    // of every commentary shown on it, and flattening them into one list&lt;br /&gt;
    // put a commentary&#039;s notes after the moola&#039;s with nothing saying which&lt;br /&gt;
    // was which - the cards were indistinguishable. Each note is filed&lt;br /&gt;
    // under the heading of the document it came from: the page&#039;s own notes&lt;br /&gt;
    // first, then one section per commentary, in the order the page shows&lt;br /&gt;
    // them. common.js wraps injected notes in .gr-teeka-notes-group with a&lt;br /&gt;
    // .gr-teeka-notes-title, which is where the heading comes from.&lt;br /&gt;
    function groupOf(li) {&lt;br /&gt;
      var wrap = li.closest ? li.closest(&#039;.gr-teeka-notes-group&#039;) : null;&lt;br /&gt;
      if (!wrap) return null;&lt;br /&gt;
      var t = wrap.querySelector(&#039;.gr-teeka-notes-title&#039;);&lt;br /&gt;
      return {&lt;br /&gt;
        slug: wrap.getAttribute(&#039;data-teeka-slug&#039;) || &#039;&#039;,&lt;br /&gt;
        title: t ? t.textContent.trim() : &#039;&#039;&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var sections = [];          // [{key, title, items: []}] in document order&lt;br /&gt;
    var byKey = {};&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var g = groupOf(li);&lt;br /&gt;
      var key = g ? g.slug : &#039;&#039;;&lt;br /&gt;
      if (!byKey[key]) {&lt;br /&gt;
        byKey[key] = { key: key, title: g ? g.title : &#039;&#039;, items: [] };&lt;br /&gt;
        sections.push(byKey[key]);&lt;br /&gt;
      }&lt;br /&gt;
      byKey[key].items.push(li);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    sections.forEach(function (sec) {&lt;br /&gt;
      if (sec.title) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title&amp;quot;&amp;gt;&#039; + esc(sec.title) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      } else if (sections.length &amp;gt; 1) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title gra-footnote-section-own&amp;quot;&amp;gt;&#039;&lt;br /&gt;
              + esc(pageOwnNotesLabel()) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      }&lt;br /&gt;
      sec.items.forEach(function (li) { html += footnoteCard(li, sec.title); });&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The heading for the page&#039;s own notes, shown only when there is at&lt;br /&gt;
  // least one commentary section to tell them apart from.&lt;br /&gt;
  function pageOwnNotesLabel() {&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return (t &amp;amp;&amp;amp; t.textContent.trim()) || &#039;This page&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function footnoteCard(li, groupTitle) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      // The commentary&#039;s name is repeated on the card itself, not only on&lt;br /&gt;
      // the section heading: a reader who has scrolled the panel, or who&lt;br /&gt;
      // arrives at a card by clicking a word in the text, sees the card&lt;br /&gt;
      // without its heading in view.&lt;br /&gt;
      return &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (groupTitle ? &#039;&amp;lt;div class=&amp;quot;gra-footnote-source&amp;quot;&amp;gt;&#039;+esc(groupTitle)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-cite-copy&#039;)) return;&lt;br /&gt;
      if ($(e.target).closest(&#039;.gra-bookmark-intext&#039;).length) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-intext&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $el = $(this), id = $el.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      copyToClipboard(buildCitation(rec).inText, function(){&lt;br /&gt;
        $el.addClass(&#039;gra-intext--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $el.removeClass(&#039;gra-intext--done&#039;); }, 1200);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-cite-copy&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $btn = $(this), id = $btn.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      var cite = buildCitation(rec);&lt;br /&gt;
      var what = $btn.attr(&#039;data-cite-what&#039;) || &#039;reference&#039;;&lt;br /&gt;
      // Only the reference carries formatting worth preserving; the in-text&lt;br /&gt;
      // form and the bare link must stay plain so they survive a paste into&lt;br /&gt;
      // the address bar.&lt;br /&gt;
      var text = cite[what] || cite.reference;&lt;br /&gt;
      var html = (what === &#039;reference&#039;) ? cite.referenceHtml : null;&lt;br /&gt;
      var was = $btn.text();&lt;br /&gt;
      copyToClipboard(text, function(){&lt;br /&gt;
        $btn.text(&#039;Copied&#039;).addClass(&#039;gra-cite-copy--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $btn.text(was).removeClass(&#039;gra-cite-copy--done&#039;); }, 1600);&lt;br /&gt;
      }, html);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
    hydrateCitationSources();&lt;br /&gt;
    // Commentary blocks arrive after the page does, so hydrate again when&lt;br /&gt;
    // they land rather than only once at boot.&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
      hydrateCitationSources(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  // The count badge is gone. It sat on the round toggle, which clips its&lt;br /&gt;
  // own overflow to stay circular, so the badge was cut off on every side&lt;br /&gt;
  // and told the reader nothing they could act on. Kept as a no-op so the&lt;br /&gt;
  // five call sites that report a change still read clearly.&lt;br /&gt;
  function updateBadge() { }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:GranthaMap.json&amp;diff=7595</id>
		<title>MediaWiki:GranthaMap.json</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:GranthaMap.json&amp;diff=7595"/>
		<updated>2026-09-30T11:22:02Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
	&amp;quot;pages&amp;quot;: {&lt;br /&gt;
		&amp;quot;Aitareya&amp;quot;: &amp;quot;UP.08.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Anubhashya&amp;quot;: &amp;quot;SP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Anuvyakhyana&amp;quot;: &amp;quot;SP.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Anuvyakhyana/Vyakhya/Nyayasudha&amp;quot;: &amp;quot;SP.01.03.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavadgitabhashya&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavadgitatatparya&amp;quot;: &amp;quot;GP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavadgitatatparya/Vyakhya/Nyayadeepika&amp;quot;: &amp;quot;GP.01.02.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavatatatparyanirnaya&amp;quot;: &amp;quot;IP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Brahmasutra&amp;quot;: &amp;quot;SP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Bruhadaranyaka&amp;quot;: &amp;quot;UP.10.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.09.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/VedeshaPadarthakaumudi&amp;quot;: &amp;quot;UP.09.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Dwadasha&amp;quot;: &amp;quot;SA.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.01.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/NrsimhacharyaDurgamavakyarthavivarana&amp;quot;: &amp;quot;UP.01.01.02.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/RaghunathaVivriti&amp;quot;: &amp;quot;UP.01.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/SrinivasaVyakhyana&amp;quot;: &amp;quot;UP.01.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.01.01.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/JayatirthaPrameyadipika&amp;quot;: &amp;quot;GP.01.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/SumatindraBhavaratnakosha&amp;quot;: &amp;quot;GP.01.01.03.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Isha&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Jayantinirnaya&amp;quot;: &amp;quot;SA.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Kandukastuti&amp;quot;: &amp;quot;SA.08&amp;quot;,&lt;br /&gt;
		&amp;quot;Karmanirnaya&amp;quot;: &amp;quot;DP.10&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha&amp;quot;: &amp;quot;UP.03.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.03.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.03.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VaradatirthaCommentary&amp;quot;: &amp;quot;UP.03.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VedeshatirthaVivarana&amp;quot;: &amp;quot;UP.03.01.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot;: &amp;quot;UP.03.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Kathalakshanam&amp;quot;: &amp;quot;DP.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Kathalakshanam/Vyakhya/Kathalakshana-tika&amp;quot;: &amp;quot;DP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Kathalakshanam/Vyakhya/Kathalakshanapanchika&amp;quot;: &amp;quot;DP.02.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Krishnamrutamaharnava&amp;quot;: &amp;quot;SA.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Mahabharatatatparyanirnaya&amp;quot;: &amp;quot;IP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Mahaitareya&amp;quot;: &amp;quot;UP.08.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Mandu&amp;quot;: &amp;quot;UP.06.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Mayavadakhandanam&amp;quot;: &amp;quot;DP.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Mithyatvanumanakhanadanam&amp;quot;: &amp;quot;DP.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Mundaka&amp;quot;: &amp;quot;UP.05.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Nakha&amp;quot;: &amp;quot;SA.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Nakha/Vyakhya/BalabodhiniNakha&amp;quot;: &amp;quot;SA.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Nakha/Vyakhya/MandabodhiniNakha&amp;quot;: &amp;quot;SA.01.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Nyayavivaranam&amp;quot;: &amp;quot;SP.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Padyamala&amp;quot;: &amp;quot;RC.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Pramaanalakshanam&amp;quot;: &amp;quot;DP.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Pramaanalakshanam/Vyakhya/Pramaanalakshana-tika&amp;quot;: &amp;quot;DP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Pramanapaddati&amp;quot;: &amp;quot;RC.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Rigbhashyam&amp;quot;: &amp;quot;RP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Sadacharasmriti&amp;quot;: &amp;quot;SA.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Satprashna&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.04.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.04.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/SrinivasaTippani&amp;quot;: &amp;quot;UP.04.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.04.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Taittiriya&amp;quot;: &amp;quot;UP.07.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara&amp;quot;: &amp;quot;UP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.02.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VaradaTika&amp;quot;: &amp;quot;UP.02.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VedeshaVivarana&amp;quot;: &amp;quot;UP.02.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VyasatirthaVivarana&amp;quot;: &amp;quot;UP.02.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/GudhakartrikaTippani&amp;quot;: &amp;quot;SA.05.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/MangaliTantrasarasangrahatippani&amp;quot;: &amp;quot;SA.05.10&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/RaghunathaTattvakanika&amp;quot;: &amp;quot;SA.05.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VasudhendraTantrasarasangrahavivarana&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VedavedyaTantrasaravivriti&amp;quot;: &amp;quot;SA.05.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VedavyasaPadabhavaprabodhini&amp;quot;: &amp;quot;SA.05.12&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VenkatanarasimhacharyaTantrasaravivriti&amp;quot;: &amp;quot;SA.05.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VenkatapatyacharyaGurvarthadidhiti&amp;quot;: &amp;quot;SA.05.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasarasangraha&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvasankhyanam&amp;quot;: &amp;quot;DP.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvasankhyanam/Vyakhya/Tattvasankhyanam-tika&amp;quot;: &amp;quot;DP.06.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvaviveka&amp;quot;: &amp;quot;DP.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvaviveka/Vyakhya/Tatvaviveka-tika&amp;quot;: &amp;quot;DP.07.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvodyota&amp;quot;: &amp;quot;DP.08&amp;quot;,&lt;br /&gt;
		&amp;quot;Upadhikhandanam&amp;quot;: &amp;quot;DP.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali&amp;quot;: &amp;quot;RC.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali/Vyakhya/Bhavadipika&amp;quot;: &amp;quot;RC.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali/Vyakhya/Bhavaprakashaha&amp;quot;: &amp;quot;RC.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali/Vyakhya/Bhavavivaranam&amp;quot;: &amp;quot;RC.02.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti&amp;quot;: &amp;quot;RC.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/BhagavantarayaLaghunigudharthaprakashika&amp;quot;: &amp;quot;RC.09.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/KurmaBhavapradipika&amp;quot;: &amp;quot;RC.09.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/SrinivasaShrinivasatirthiya&amp;quot;: &amp;quot;RC.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/VedatmaKavikarnamrita&amp;quot;: &amp;quot;RC.09.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/VishwapatiBalabodhini&amp;quot;: &amp;quot;RC.09.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Vishnutattvavinirnaya&amp;quot;: &amp;quot;DP.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Yamakabharatam&amp;quot;: &amp;quot;IP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Yamakabharatam/Vyakhya/YamakabharataTikaYadupatyam&amp;quot;: &amp;quot;IP.01.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Yatipranavavakalpa&amp;quot;: &amp;quot;SA.06&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;works&amp;quot;: {&lt;br /&gt;
		&amp;quot;DP.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;प्रमाणलक्षणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Pramāṇalakṣaṇa&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;न्यायकल्पलता&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Nyāyakalpalatā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कथालक्षणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kathālakṣaṇa&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.02.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कथालक्षणटीका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kathālakṣaṇaṭīkā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीपद्मनाभतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Padmanābhatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.02.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कथालक्षणपञ्चिका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kathālakṣaṇapañcikā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;मायावादखण्डनम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Māyāvādakhaṇḍana&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;मिथ्यात्वानुमानखण्डनम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Mithyātvānumānakhaṇḍana&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.05&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;उपाधिखण्डनम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Upādhikhaṇḍana&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.06&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तत्त्वसङ्ख्यानम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tattvasaṅkhyāna&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.06.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तत्त्वसङ्ख्यानटीका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tattvasaṅkhyānaṭīkā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.07&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तत्त्वविवेकः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tattvaviveka&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.07.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तत्त्वविवेकटीका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tattvavivekaṭīkā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.08&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तत्त्वोद्योतः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tattvodyota&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.09&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;विष्णुतत्त्वविनिर्णयः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Viṣṇutattvavinirṇaya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;DP.10&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कर्मनिर्णयः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Karmanirṇaya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;GP.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;गीताभाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Gītābhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;GP.01.01.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;प्रमेयदीपिका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Prameyadīpikā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;GP.01.01.03.05&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;भावरत्नकोशः (गीता)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bhāvaratnakośa (on Gītā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीसुमतीन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Sumatīndratīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;GP.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;गीतातात्पर्यनिर्णयः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Gītātātparyanirṇaya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;GP.01.02.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;न्यायदीपिका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Nyāyadīpikā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;IP.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;महाभारततात्पर्यनिर्णयः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Mahābhāratatātparyanirṇaya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;IP.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;यमकभारतम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Yamakabhārata&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;IP.01.02.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;यमकभारतटीका / यादुपत्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Yamakabhārataṭīkā (Yādupatyam)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीयदुपत्याचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Yadupatyācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;IP.02.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;भागवततात्पर्यनिर्णयः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bhāgavatatātparyanirṇaya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;प्रमाणपद्धतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Pramāṇapaddhati&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वादावली&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vādāvalī&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.02.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;भावदीपिका (वादावली)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bhāvadīpikā (on Vādāvalī)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीराघवेन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Rāghavendratīrtha (Guru Rayaru)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.02.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;भावप्रकाशः (वादावली)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bhāvaprakāśa (on Vādāvalī)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीनिवासतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Śrīnivāsatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.02.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;भावविवरणम् (वादावली)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bhāvavivaraṇa (on Vādāvalī)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीउमर्जी कृष्णाचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Umarjī Kṛṣṇācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;पद्यमाला&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Padyamālā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.09&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वायुस्तुतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vāyustutiḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीत्रिविक्रमपण्डिताचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Trivikrama Paṇḍitācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.09.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वायुस्तुतिमण्डनम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vāyustutimaṇḍanam&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीसत्यप्रमोदतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Satyapramodatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.09.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वायुस्तुतिव्याख्या&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vāyustutivyākhyā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीविश्वपतितीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Viśvapatitīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.09.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वायुस्तुतिव्याख्या&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vāyustutivyākhyā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेदात्मतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vedātmatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.09.06&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वायुस्तुतिव्याख्या&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vāyustutivyākhyā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीकूर्म नरहर्याचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Kūrma Naraharyācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RC.09.07&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;वायुस्तुतिव्याख्या&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Vāyustutivyākhyā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीभगवन्तरायः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Bhagavantarāya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;RP.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ऋग्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Ṛgbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;नखस्तुतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Nakhastuti&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;बालबोधिनी (नखस्तुति)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bālabodhinī (on Nakhastuti)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीविश्वपतितीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Viśvapatitīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.01.07&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;मन्दबोधिनी (नखस्तुति)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Mandabodhinī (on Nakhastuti)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीशेषाचार्यः (छलारिनरसिंहाचार्यशिष्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Śeṣācārya (Disciple of Chalāri Narasiṃhācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;द्वादशस्तोत्रम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Dvādaśastotra&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;सदाचारस्मृतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Sadācārasmṛti&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कृष्णामृतमहार्णवः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kṛṣṇāmṛtamahārṇava&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तन्त्रसारसङ्ग्रहः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tantrasārasaṅgraha&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तत्त्वकणिका (तन्त्रसार)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tattvakaṇikā (on Tantrasāra)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीरघुनाथतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Raghunāthatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.05&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तन्त्रसारवृत्तिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tantrasāravṛttiḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेदवेद्यतीर्थशिष्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vedavedyatīrtha-śiṣya (disciple of Vedavedyatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.06&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तन्त्रसारसङ्ग्रहटिप्पणी (गूढकर्तृका)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tantrasārasaṅgrahaṭippaṇī (Gūḍhakartṛkā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;अज्ञातकर्तृका&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Ajñātakartṛkā (Anonymous)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.07&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;गुर्वर्थदीधितिः (तन्त्रसारटीका)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Gurvarthadīdhitiḥ (Tantrasāra-ṭīkā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेङ्कटपत्याचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Veṅkaṭapatyācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.09&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तन्त्रसारविवृतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tantrasāravivṛtiḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेङ्कटनरसिंहाचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Veṅkaṭanarasiṃhācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.10&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तन्त्रसारसङ्ग्रहटिप्पणी (मङ्गलीया)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Tantrasārasaṅgrahaṭippaṇī (Maṅgalīyā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमङ्गलीधर्माचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Maṅgalī Dharmācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.05.12&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;पदभावप्रबोधिनी (तन्त्रसार)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Padabhāvaprabodhinī (on Tantrasāra)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेदव्यासाचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vedavyāsācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.06&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;यतिप्रणवकल्पः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Yatipraṇavakalpa&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.07&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;जयन्तीनिर्णयः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Jayantīnirṇaya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SA.08&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कन्दुकस्तुतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kandukastuti&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SP.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ब्रह्मसूत्रभाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Brahmasūtrabhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SP.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;अणुभाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Aṇubhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SP.01.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;अनुव्याख्यानम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Anuvyākhyāna&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SP.01.03.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;न्यायसुधा&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Nyāyasudhā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;SP.01.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;न्यायविवरणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Nyāyavivaraṇa&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्योपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्योपनिषद्भाष्यटीका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyopaniṣadbhāṣyaṭīkā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.01.02.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्यभाष्यटीकाप्रकाशिका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyabhāṣyaṭīkāprakāśikā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवादिराजतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vādirājatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.01.02.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्यभाष्यटीकादुर्गमवाक्यार्थविवरणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyabhāṣyaṭīkādurgamavākyārthavivaraṇam&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीछलारि नरसिंहाचार्यः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Chalāri Narasiṃhācārya&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.01.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्योपनिषद्भाष्यविवृतिः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyopaniṣadbhāṣyavivṛtiḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीरघुनाथतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Raghunāthatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.01.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्योपनिषद्भाष्यव्याख्यानम् (श्रीनिवासतीर्थीयम्)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyopaniṣadbhāṣyavyākhyānam (Śrīnivāsatīrthīyam)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीनिवासतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Śrīnivāsatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ईशावास्योपनिषत्खण्डार्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Īśāvāsyopaniṣatkhaṇḍārthaḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीराघवेन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Rāghavendratīrtha (Guru Rayaru)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.02.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तलवकारोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Talavakāropaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.02.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;प्रकाशिका (तलवकार)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Prakāśikā (on Talavakāra)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवादिराजतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vādirājatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.02.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तलवकारोपनिषद्भाष्यविवरणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Talavakāropaniṣadbhāṣyavivaraṇam&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीव्यासतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vyāsatīrtha (Vyāsarāja)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.02.01.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तलवकारोपनिषद्भाष्यटीका (वरदतीर्थीया)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Talavakāropaniṣadbhāṣyaṭīkā (Varadatīrthīyā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवरदतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Varadatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.02.01.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तलवकारोपनिषद्भाष्यविवरणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Talavakāropaniṣadbhāṣyavivaraṇam&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेदेशतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vedeśatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.02.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तलवकारोपनिषत्खण्डार्थः (केनोपनिषत्)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Talavakāropaniṣatkhaṇḍārthaḥ (Kenopaniṣad)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीराघवेन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Rāghavendratīrtha (Guru Rayaru)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.03.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कठोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kaṭhopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.03.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;काठकोपनिषद्भाष्यप्रकाशिका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kāṭhakopaniṣadbhāṣyaprakāśikā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवादिराजतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vādirājatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.03.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कठोपनिषद्भाष्यविवरणम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kaṭhopaniṣadbhāṣyavivaraṇa&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीव्यासतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vyāsatīrtha (Vyāsarāja)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.03.01.04&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;काठकोपनिषद्भाष्यटीका (वरदतीर्थीया)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kāṭhakopaniṣadbhāṣyaṭīkā (Varadatīrthīyā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवरदतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Varadatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.03.01.05&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;काठकोपनिषद्भाष्यविवरणम् (वेदेशीयम्)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kāṭhakopaniṣadbhāṣyavivaraṇam (Vedeśīyam)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेदेशतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vedeśatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.03.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;कठोपनिषत्खण्डार्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Kaṭhopaniṣatkhaṇḍārthaḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीराघवेन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Rāghavendratīrtha (Guru Rayaru)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.04.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;षट्प्रश्नोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Ṣaṭpraśnopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.04.01.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;षट्प्रश्नोपनिषद्भाष्यटीका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Ṣaṭpraśnopaniṣadbhāṣyaṭīkā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीजयतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Jayatīrtha (Ṭīkācārya)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.04.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;षट्प्रश्नोपनिषद्भाष्यप्रकाशिका&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Ṣaṭpraśnopaniṣadbhāṣyaprakāśikā&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवादिराजतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vādirājatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.04.01.03&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;षट्प्रश्नोपनिषद्भाष्यटिप्पणी (श्रीनिवासतीर्थीया)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Ṣaṭpraśnopaniṣadbhāṣyaṭippaṇī (Śrīnivāsatīrthīyā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीनिवासतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Śrīnivāsatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.04.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;षट्प्रश्नोपनिषत्खण्डार्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Ṣaṭpraśnopaniṣatkhaṇḍārthaḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीराघवेन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Rāghavendratīrtha (Guru Rayaru)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.05.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;मुण्डकोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Muṇḍakopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.06.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;माण्डूक्योपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Māṇḍūkyopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.07.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;तैत्तिरीयोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Taittirīyopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.08.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;ऐतरेयोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Aitareyopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.09.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;छान्दोग्योपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Chāndogyopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.09.01.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;छान्दोग्योपनिषद्भाष्यटीका (वेदेशीया)&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Chāndogyopaniṣadbhāṣyaṭīkā (Vedeśīyā)&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीवेदेशतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Vedeśatīrtha&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.09.02&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;छान्दोग्योपनिषत्खण्डार्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Chāndogyopaniṣatkhaṇḍārthaḥ&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीराघवेन्द्रतीर्थः&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrī Rāghavendratīrtha (Guru Rayaru)&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;UP.10.01&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;बृहदारण्यकोपनिषद्भाष्यम्&amp;quot;,&lt;br /&gt;
			&amp;quot;nameIAST&amp;quot;: &amp;quot;Bṛhadāraṇyakopaniṣadbhāṣya&amp;quot;,&lt;br /&gt;
			&amp;quot;author&amp;quot;: &amp;quot;श्रीमदानन्दतीर्थः (मध्वाचार्यः)&amp;quot;,&lt;br /&gt;
			&amp;quot;authorIAST&amp;quot;: &amp;quot;Śrīman Madhvācārya (Ānandatīrtha)&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;locators&amp;quot;: {&lt;br /&gt;
		&amp;quot;Katha&amp;quot;: &amp;quot;C=Adhyāya,S=Vallī,V=Mantra,B=Bhāṣya&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7594</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7594"/>
		<updated>2026-09-30T11:21:39Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont, limit) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: limit, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                // The API answers 200 with an {error:...} body, so a failed&lt;br /&gt;
                // query is not a rejected promise. Treat it as one.&lt;br /&gt;
                if (j &amp;amp;&amp;amp; j.error) throw new Error(j.error.code || &#039;api error&#039;);&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) {&lt;br /&gt;
                    return page(j[&#039;continue&#039;].rccontinue, limit);&lt;br /&gt;
                }&lt;br /&gt;
                return all;&lt;br /&gt;
            }).catch(function (err) {&lt;br /&gt;
                // recentchanges over a date range is the most expensive query&lt;br /&gt;
                // this site makes, and on a busy or constrained database it&lt;br /&gt;
                // can come back as a DBQueryError. Halve the window and retry&lt;br /&gt;
                // once; failing that, return nothing. A &amp;quot;what&#039;s new&amp;quot; list is&lt;br /&gt;
                // not worth breaking the page for.&lt;br /&gt;
                if (limit &amp;gt; 100) return page(cont, 100);&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[gr] recent changes unavailable: &#039; +&lt;br /&gt;
                                 (err &amp;amp;&amp;amp; err.message || err));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null, 500);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([&lt;br /&gt;
            fetchChanges().catch(function () { return []; }),&lt;br /&gt;
            fetchCatalogue().catch(function () { return null; })&lt;br /&gt;
        ]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // ---- CONFIGURE ME -------------------------------------------------&lt;br /&gt;
    // Where the Grantha library&#039;s REST API lives. Same-origin by default,&lt;br /&gt;
    // which needs no CORS - but the library is its own app, so unless it is&lt;br /&gt;
    // reverse-proxied under the wiki&#039;s domain this must be its real host:&lt;br /&gt;
    //     var API_BASE = &#039;https://library.example.org/api/granthas&#039;;&lt;br /&gt;
    // On another host the library must send&lt;br /&gt;
    //     Access-Control-Allow-Origin: https://anandamakaranda.in&lt;br /&gt;
    // or the browser will refuse the reply and every citation will fall&lt;br /&gt;
    // back to the author baked into the page (usually none).&lt;br /&gt;
    var API_BASE = window.GR_LIBRARY_API || &#039;/api/granthas&#039;;&lt;br /&gt;
    // Set to false while the library is not yet reachable: no request is&lt;br /&gt;
    // made at all, and citations use whatever the page itself carries.&lt;br /&gt;
    // Flip it back to true once API_BASE resolves.&lt;br /&gt;
    var LIBRARY_ENABLED = true;&lt;br /&gt;
    // -------------------------------------------------------------------&lt;br /&gt;
    var API = API_BASE.replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var TTL = 6 * 60 * 60 * 1000;                 // names change rarely&lt;br /&gt;
    var mem = {};&lt;br /&gt;
    var warned = false;&lt;br /&gt;
    // Once the library has answered &amp;quot;not here&amp;quot;, every further call will get&lt;br /&gt;
    // the same answer. Stop asking: a wrong or missing API base otherwise&lt;br /&gt;
    // fills the network log with one 404 per work on the page.&lt;br /&gt;
    var libraryDown = !LIBRARY_ENABLED;&lt;br /&gt;
&lt;br /&gt;
    function cacheGet(id) {&lt;br /&gt;
        if (mem[id]) return mem[id];&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:&#039; + id);&lt;br /&gt;
            if (!raw) return null;&lt;br /&gt;
            var o = JSON.parse(raw);&lt;br /&gt;
            if (!o || (Date.now() - o.t) &amp;gt; TTL) return null;&lt;br /&gt;
            mem[id] = o.v;&lt;br /&gt;
            return o.v;&lt;br /&gt;
        } catch (e) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function cacheSet(id, v) {&lt;br /&gt;
        mem[id] = v;&lt;br /&gt;
        try { sessionStorage.setItem(&#039;gr-lib:&#039; + id, JSON.stringify({ t: Date.now(), v: v })); }&lt;br /&gt;
        catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        // The map page carries the names for every work this wiki uses, so&lt;br /&gt;
        // normally nothing is fetched from the library at all. That matters&lt;br /&gt;
        // because the library is someone else&#039;s server: we cannot add CORS&lt;br /&gt;
        // headers to it, and a cross-origin call we cannot configure is a&lt;br /&gt;
        // call that will not work. The live API stays as an enhancement for&lt;br /&gt;
        // whenever it IS reachable.&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var w = (m.works || {})[id];&lt;br /&gt;
            if (w &amp;amp;&amp;amp; (w.name || w.nameIAST)) {&lt;br /&gt;
                var v = { id: id, name: w.name || &#039;&#039;, nameIAST: w.nameIAST || &#039;&#039;,&lt;br /&gt;
                          author: w.author || &#039;&#039;, authorIAST: w.authorIAST || &#039;&#039;,&lt;br /&gt;
                          category: w.category || &#039;&#039;, prasthana: w.prasthana || &#039;&#039; };&lt;br /&gt;
                cacheSet(id, v);&lt;br /&gt;
                return v;&lt;br /&gt;
            }&lt;br /&gt;
            return fetchFromLibrary(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchFromLibrary(id) {&lt;br /&gt;
        if (libraryDown) return Promise.resolve(null);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            // A 404 or 5xx means the base URL is wrong or the service is&lt;br /&gt;
            // down - not that this one work is missing - so trip the breaker.&lt;br /&gt;
            if (!r.ok) {&lt;br /&gt;
                if (r.status === 404 || r.status &amp;gt;= 500) libraryDown = true;&lt;br /&gt;
                throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            }&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function (err) {&lt;br /&gt;
            // Offline, blocked by CORS, wrong host, or the id is not in the&lt;br /&gt;
            // library yet. The page&#039;s own data-author/data-doc-title stand&lt;br /&gt;
            // in, so a citation is never blocked on this request - but an&lt;br /&gt;
            // unreachable library looks exactly like a work with no author,&lt;br /&gt;
            // so say which it is, once.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            libraryDown = true;&lt;br /&gt;
            if (!warned) {&lt;br /&gt;
                warned = true;&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[grantha] could not read the library at &#039; + API +&lt;br /&gt;
                        &#039; (asked for &amp;quot;&#039; + id + &#039;&amp;quot;): &#039; + (err &amp;amp;&amp;amp; err.message || err) +&lt;br /&gt;
                        &#039;. Citations will use whatever the page itself carries. &#039; +&lt;br /&gt;
                        &#039;Set window.GR_LIBRARY_API to the library host, and allow &#039; +&lt;br /&gt;
                        &#039;this origin via Access-Control-Allow-Origin.&#039;);&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            }&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:map&#039;);&lt;br /&gt;
            var o = raw &amp;amp;&amp;amp; JSON.parse(raw);&lt;br /&gt;
            if (o &amp;amp;&amp;amp; (Date.now() - o.t) &amp;lt; TTL) cached = o.v;&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        if (cached) { mapPromise = Promise.resolve(cached); return mapPromise; }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages:{...},&lt;br /&gt;
                // locators:{...}} when division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m, locators: {} };&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try { sessionStorage.setItem(&#039;gr-lib:map&#039;, JSON.stringify({ t: Date.now(), v: m })); }&lt;br /&gt;
                catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (libraryDown) return Promise.resolve({});&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
              if (!r.ok) { libraryDown = true; return null; }&lt;br /&gt;
              return r.json();&lt;br /&gt;
          })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        // lookup() answers from the map page without a network call, so&lt;br /&gt;
        // resolving fifty works costs one shared map fetch, not fifty&lt;br /&gt;
        // requests. Only ids the map does not name reach the library.&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7593</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7593"/>
		<updated>2026-09-30T11:10:58Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont, limit) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: limit, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                // The API answers 200 with an {error:...} body, so a failed&lt;br /&gt;
                // query is not a rejected promise. Treat it as one.&lt;br /&gt;
                if (j &amp;amp;&amp;amp; j.error) throw new Error(j.error.code || &#039;api error&#039;);&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) {&lt;br /&gt;
                    return page(j[&#039;continue&#039;].rccontinue, limit);&lt;br /&gt;
                }&lt;br /&gt;
                return all;&lt;br /&gt;
            }).catch(function (err) {&lt;br /&gt;
                // recentchanges over a date range is the most expensive query&lt;br /&gt;
                // this site makes, and on a busy or constrained database it&lt;br /&gt;
                // can come back as a DBQueryError. Halve the window and retry&lt;br /&gt;
                // once; failing that, return nothing. A &amp;quot;what&#039;s new&amp;quot; list is&lt;br /&gt;
                // not worth breaking the page for.&lt;br /&gt;
                if (limit &amp;gt; 100) return page(cont, 100);&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[gr] recent changes unavailable: &#039; +&lt;br /&gt;
                                 (err &amp;amp;&amp;amp; err.message || err));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null, 500);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([&lt;br /&gt;
            fetchChanges().catch(function () { return []; }),&lt;br /&gt;
            fetchCatalogue().catch(function () { return null; })&lt;br /&gt;
        ]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // ---- CONFIGURE ME -------------------------------------------------&lt;br /&gt;
    // Where the Grantha library&#039;s REST API lives. Same-origin by default,&lt;br /&gt;
    // which needs no CORS - but the library is its own app, so unless it is&lt;br /&gt;
    // reverse-proxied under the wiki&#039;s domain this must be its real host:&lt;br /&gt;
    //     var API_BASE = &#039;https://library.example.org/api/granthas&#039;;&lt;br /&gt;
    // On another host the library must send&lt;br /&gt;
    //     Access-Control-Allow-Origin: https://anandamakaranda.in&lt;br /&gt;
    // or the browser will refuse the reply and every citation will fall&lt;br /&gt;
    // back to the author baked into the page (usually none).&lt;br /&gt;
    var API_BASE = window.GR_LIBRARY_API || &#039;/api/granthas&#039;;&lt;br /&gt;
    // Set to false while the library is not yet reachable: no request is&lt;br /&gt;
    // made at all, and citations use whatever the page itself carries.&lt;br /&gt;
    // Flip it back to true once API_BASE resolves.&lt;br /&gt;
    var LIBRARY_ENABLED = true;&lt;br /&gt;
    // -------------------------------------------------------------------&lt;br /&gt;
    var API = API_BASE.replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var TTL = 6 * 60 * 60 * 1000;                 // names change rarely&lt;br /&gt;
    var mem = {};&lt;br /&gt;
    var warned = false;&lt;br /&gt;
    // Once the library has answered &amp;quot;not here&amp;quot;, every further call will get&lt;br /&gt;
    // the same answer. Stop asking: a wrong or missing API base otherwise&lt;br /&gt;
    // fills the network log with one 404 per work on the page.&lt;br /&gt;
    var libraryDown = !LIBRARY_ENABLED;&lt;br /&gt;
&lt;br /&gt;
    function cacheGet(id) {&lt;br /&gt;
        if (mem[id]) return mem[id];&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:&#039; + id);&lt;br /&gt;
            if (!raw) return null;&lt;br /&gt;
            var o = JSON.parse(raw);&lt;br /&gt;
            if (!o || (Date.now() - o.t) &amp;gt; TTL) return null;&lt;br /&gt;
            mem[id] = o.v;&lt;br /&gt;
            return o.v;&lt;br /&gt;
        } catch (e) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function cacheSet(id, v) {&lt;br /&gt;
        mem[id] = v;&lt;br /&gt;
        try { sessionStorage.setItem(&#039;gr-lib:&#039; + id, JSON.stringify({ t: Date.now(), v: v })); }&lt;br /&gt;
        catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        if (libraryDown) return Promise.resolve(null);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            // A 404 or 5xx means the base URL is wrong or the service is&lt;br /&gt;
            // down - not that this one work is missing - so trip the breaker.&lt;br /&gt;
            if (!r.ok) {&lt;br /&gt;
                if (r.status === 404 || r.status &amp;gt;= 500) libraryDown = true;&lt;br /&gt;
                throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            }&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function (err) {&lt;br /&gt;
            // Offline, blocked by CORS, wrong host, or the id is not in the&lt;br /&gt;
            // library yet. The page&#039;s own data-author/data-doc-title stand&lt;br /&gt;
            // in, so a citation is never blocked on this request - but an&lt;br /&gt;
            // unreachable library looks exactly like a work with no author,&lt;br /&gt;
            // so say which it is, once.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            libraryDown = true;&lt;br /&gt;
            if (!warned) {&lt;br /&gt;
                warned = true;&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[grantha] could not read the library at &#039; + API +&lt;br /&gt;
                        &#039; (asked for &amp;quot;&#039; + id + &#039;&amp;quot;): &#039; + (err &amp;amp;&amp;amp; err.message || err) +&lt;br /&gt;
                        &#039;. Citations will use whatever the page itself carries. &#039; +&lt;br /&gt;
                        &#039;Set window.GR_LIBRARY_API to the library host, and allow &#039; +&lt;br /&gt;
                        &#039;this origin via Access-Control-Allow-Origin.&#039;);&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            }&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:map&#039;);&lt;br /&gt;
            var o = raw &amp;amp;&amp;amp; JSON.parse(raw);&lt;br /&gt;
            if (o &amp;amp;&amp;amp; (Date.now() - o.t) &amp;lt; TTL) cached = o.v;&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        if (cached) { mapPromise = Promise.resolve(cached); return mapPromise; }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages:{...},&lt;br /&gt;
                // locators:{...}} when division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m, locators: {} };&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try { sessionStorage.setItem(&#039;gr-lib:map&#039;, JSON.stringify({ t: Date.now(), v: m })); }&lt;br /&gt;
                catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (libraryDown) return Promise.resolve({});&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
              if (!r.ok) { libraryDown = true; return null; }&lt;br /&gt;
              return r.json();&lt;br /&gt;
          })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        var missing = want.filter(function (i) { return !cacheGet(i); });&lt;br /&gt;
        if (missing.length &amp;gt; 5) {&lt;br /&gt;
            return catalogue().then(function (by) {&lt;br /&gt;
                var out = {};&lt;br /&gt;
                want.forEach(function (i) { out[i] = by[i] || cacheGet(i) || null; });&lt;br /&gt;
                return out;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7592</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7592"/>
		<updated>2026-09-30T11:09:26Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont, limit) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: limit, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                // The API answers 200 with an {error:...} body, so a failed&lt;br /&gt;
                // query is not a rejected promise. Treat it as one.&lt;br /&gt;
                if (j &amp;amp;&amp;amp; j.error) throw new Error(j.error.code || &#039;api error&#039;);&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) {&lt;br /&gt;
                    return page(j[&#039;continue&#039;].rccontinue, limit);&lt;br /&gt;
                }&lt;br /&gt;
                return all;&lt;br /&gt;
            }).catch(function (err) {&lt;br /&gt;
                // recentchanges over a date range is the most expensive query&lt;br /&gt;
                // this site makes, and on a busy or constrained database it&lt;br /&gt;
                // can come back as a DBQueryError. Halve the window and retry&lt;br /&gt;
                // once; failing that, return nothing. A &amp;quot;what&#039;s new&amp;quot; list is&lt;br /&gt;
                // not worth breaking the page for.&lt;br /&gt;
                if (limit &amp;gt; 100) return page(cont, 100);&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[gr] recent changes unavailable: &#039; +&lt;br /&gt;
                                 (err &amp;amp;&amp;amp; err.message || err));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null, 500);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([&lt;br /&gt;
            fetchChanges().catch(function () { return []; }),&lt;br /&gt;
            fetchCatalogue().catch(function () { return null; })&lt;br /&gt;
        ]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // ---- CONFIGURE ME -------------------------------------------------&lt;br /&gt;
    // Where the Grantha library&#039;s REST API lives. Same-origin by default,&lt;br /&gt;
    // which needs no CORS - but the library is its own app, so unless it is&lt;br /&gt;
    // reverse-proxied under the wiki&#039;s domain this must be its real host:&lt;br /&gt;
    //     var API_BASE = &#039;https://library.example.org/api/granthas&#039;;&lt;br /&gt;
    // On another host the library must send&lt;br /&gt;
    //     Access-Control-Allow-Origin: https://anandamakaranda.in&lt;br /&gt;
    // or the browser will refuse the reply and every citation will fall&lt;br /&gt;
    // back to the author baked into the page (usually none).&lt;br /&gt;
    var API_BASE = window.GR_LIBRARY_API || &#039;/api/granthas&#039;;&lt;br /&gt;
    // -------------------------------------------------------------------&lt;br /&gt;
    var API = API_BASE.replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var TTL = 6 * 60 * 60 * 1000;                 // names change rarely&lt;br /&gt;
    var mem = {};&lt;br /&gt;
    var warned = false;&lt;br /&gt;
    // Once the library has answered &amp;quot;not here&amp;quot;, every further call will get&lt;br /&gt;
    // the same answer. Stop asking: a wrong or missing API base otherwise&lt;br /&gt;
    // fills the network log with one 404 per work on the page.&lt;br /&gt;
    var libraryDown = false;&lt;br /&gt;
&lt;br /&gt;
    function cacheGet(id) {&lt;br /&gt;
        if (mem[id]) return mem[id];&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:&#039; + id);&lt;br /&gt;
            if (!raw) return null;&lt;br /&gt;
            var o = JSON.parse(raw);&lt;br /&gt;
            if (!o || (Date.now() - o.t) &amp;gt; TTL) return null;&lt;br /&gt;
            mem[id] = o.v;&lt;br /&gt;
            return o.v;&lt;br /&gt;
        } catch (e) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function cacheSet(id, v) {&lt;br /&gt;
        mem[id] = v;&lt;br /&gt;
        try { sessionStorage.setItem(&#039;gr-lib:&#039; + id, JSON.stringify({ t: Date.now(), v: v })); }&lt;br /&gt;
        catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        if (libraryDown) return Promise.resolve(null);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            // A 404 or 5xx means the base URL is wrong or the service is&lt;br /&gt;
            // down - not that this one work is missing - so trip the breaker.&lt;br /&gt;
            if (!r.ok) {&lt;br /&gt;
                if (r.status === 404 || r.status &amp;gt;= 500) libraryDown = true;&lt;br /&gt;
                throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            }&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function (err) {&lt;br /&gt;
            // Offline, blocked by CORS, wrong host, or the id is not in the&lt;br /&gt;
            // library yet. The page&#039;s own data-author/data-doc-title stand&lt;br /&gt;
            // in, so a citation is never blocked on this request - but an&lt;br /&gt;
            // unreachable library looks exactly like a work with no author,&lt;br /&gt;
            // so say which it is, once.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            libraryDown = true;&lt;br /&gt;
            if (!warned) {&lt;br /&gt;
                warned = true;&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[grantha] could not read the library at &#039; + API +&lt;br /&gt;
                        &#039; (asked for &amp;quot;&#039; + id + &#039;&amp;quot;): &#039; + (err &amp;amp;&amp;amp; err.message || err) +&lt;br /&gt;
                        &#039;. Citations will use whatever the page itself carries. &#039; +&lt;br /&gt;
                        &#039;Set window.GR_LIBRARY_API to the library host, and allow &#039; +&lt;br /&gt;
                        &#039;this origin via Access-Control-Allow-Origin.&#039;);&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            }&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:map&#039;);&lt;br /&gt;
            var o = raw &amp;amp;&amp;amp; JSON.parse(raw);&lt;br /&gt;
            if (o &amp;amp;&amp;amp; (Date.now() - o.t) &amp;lt; TTL) cached = o.v;&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        if (cached) { mapPromise = Promise.resolve(cached); return mapPromise; }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages:{...},&lt;br /&gt;
                // locators:{...}} when division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m, locators: {} };&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try { sessionStorage.setItem(&#039;gr-lib:map&#039;, JSON.stringify({ t: Date.now(), v: m })); }&lt;br /&gt;
                catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (libraryDown) return Promise.resolve({});&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
              if (!r.ok) { libraryDown = true; return null; }&lt;br /&gt;
              return r.json();&lt;br /&gt;
          })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        var missing = want.filter(function (i) { return !cacheGet(i); });&lt;br /&gt;
        if (missing.length &amp;gt; 5) {&lt;br /&gt;
            return catalogue().then(function (by) {&lt;br /&gt;
                var out = {};&lt;br /&gt;
                want.forEach(function (i) { out[i] = by[i] || cacheGet(i) || null; });&lt;br /&gt;
                return out;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7591</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7591"/>
		<updated>2026-09-30T11:05:53Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  /* CHANGED from inline-flex + vertical-align:middle. An inline-flex box&lt;br /&gt;
     aligned &amp;quot;middle&amp;quot; is positioned by its own height against the parent&#039;s&lt;br /&gt;
     x-height, so the whole control - checkbox AND label together - sat&lt;br /&gt;
     progressively lower than the text beside it as the reader enlarged the&lt;br /&gt;
     type (measured: 1.8px out at 16px, 3.6px at 30px). Left as plain&lt;br /&gt;
     inline, the label is ordinary text and lines up with its neighbours by&lt;br /&gt;
     definition; only the checkbox needs aligning, and it does that with an&lt;br /&gt;
     em-based offset that holds at any size. */&lt;br /&gt;
  display: inline;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  /* Sized in em so it keeps pace with A- / A+ rather than staying a fixed&lt;br /&gt;
     15px box against text that grows; clamped so it stays a sensible&lt;br /&gt;
     target at both extremes.&lt;br /&gt;
     Alignment was measured rather than guessed. vertical-align:middle is&lt;br /&gt;
     the only value that stays consistent as the type size changes (it is&lt;br /&gt;
     computed from the font&#039;s x-height), but it sits about 2.5px low&lt;br /&gt;
     against this line-height at every size - so that constant bias is&lt;br /&gt;
     taken back with `top`, giving under a pixel of error from 16px to&lt;br /&gt;
     30px. The element is already position:relative for its tick. */&lt;br /&gt;
  width: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  height: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  min-width: 13px; min-height: 13px;&lt;br /&gt;
  margin: 0 6px 0 0; padding: 0; cursor: pointer;&lt;br /&gt;
  vertical-align: middle; top: -2.5px;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  /* Placed and sized in % of the box, so the tick follows the em-based&lt;br /&gt;
     box instead of staying a 4x8px mark inside a checkbox that grew. */&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 32%; top: 8%;&lt;br /&gt;
  width: 26%; height: 55%;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The annotation count badge was removed (it was clipped by the round&lt;br /&gt;
   toggle&#039;s own overflow). This keeps it hidden for anyone still served a&lt;br /&gt;
   cached copy of the old gadget. */&lt;br /&gt;
#gra-toggle-badge { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── LINK LANDING ───────────────────────────────────────────────────&lt;br /&gt;
   Where a copied id or a bookmark citation link lands. The flash used to&lt;br /&gt;
   be injected by copyId.js; it lives here so the landing still reads when&lt;br /&gt;
   only common.js is loaded. */&lt;br /&gt;
.gid-target {&lt;br /&gt;
  animation: gid-target-flash 2.4s ease-out 1;&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
}&lt;br /&gt;
@keyframes gid-target-flash {&lt;br /&gt;
  0%, 55% { background: rgba(181,69,27,0.14); box-shadow: 0 0 0 4px rgba(181,69,27,0.10); }&lt;br /&gt;
  100%    { background: transparent; box-shadow: none; }&lt;br /&gt;
}&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .gid-target { animation: none; background: rgba(181,69,27,0.14); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── BOOKMARK CITATION ──────────────────────────────────────────────&lt;br /&gt;
   The reference a bookmark produces, shown on its card and copyable in&lt;br /&gt;
   one click. Set small and in the UI face deliberately: it is apparatus&lt;br /&gt;
   for the reader to take away, not part of the text. */&lt;br /&gt;
.gra-bookmark-cite {&lt;br /&gt;
  margin-top: 6px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.54), 15px);&lt;br /&gt;
  line-height: 1.45; color: #6b5b52;&lt;br /&gt;
  overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
/* The reference hangs its continuation lines, the way a reference list&lt;br /&gt;
   does on paper. The link inside it stays on the citation&#039;s own colour&lt;br /&gt;
   until hovered - it is a citation, not a navigation control. */&lt;br /&gt;
.gra-bookmark-cite { padding-left: 1.4em; text-indent: -1.4em; }&lt;br /&gt;
.gra-bookmark-cite a { color: inherit; text-decoration: none; border-bottom: 1px dotted #c3b3a9; }&lt;br /&gt;
.gra-bookmark-cite a:hover { color: #b5451b; border-bottom-color: #b5451b; }&lt;br /&gt;
.gra-bookmark-cite i { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
.gra-bookmark-intext {&lt;br /&gt;
  margin-top: 4px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(10px, calc(var(--gr-font-size, 22px) * 0.48), 13px);&lt;br /&gt;
  color: #8a7a70;&lt;br /&gt;
  overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gra-cite-locator { color: #a08b7e; }&lt;br /&gt;
&lt;br /&gt;
/* The in-text line copies on click, so it reads as something you can&lt;br /&gt;
   press without adding another button to the card. */&lt;br /&gt;
.gra-bookmark-intext { cursor: pointer; border-radius: 4px; transition: background .15s, color .15s; }&lt;br /&gt;
.gra-bookmark-intext:hover { background: #fff5f2; color: #6b5b52; }&lt;br /&gt;
.gra-intext--done { background: #e6f2ea; color: #2e6e40; }&lt;br /&gt;
.gra-intext--done .gra-cite-locator { color: #2e6e40; }&lt;br /&gt;
&lt;br /&gt;
.gra-cite-actions { display: flex; flex-wrap: wrap; gap: 6px; }&lt;br /&gt;
&lt;br /&gt;
.gra-cite-copy {&lt;br /&gt;
  margin-top: 6px; padding: 3px 10px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  background: #fff5f2; border: 1px solid #f0cfc4; border-radius: 20px;&lt;br /&gt;
  cursor: pointer; transition: background .15s, color .15s, border-color .15s;&lt;br /&gt;
}&lt;br /&gt;
.gra-cite-copy:hover { background: #b5451b; color: #fff; border-color: #b5451b; }&lt;br /&gt;
.gra-cite-copy--done { background: #2e6e40; color: #fff; border-color: #2e6e40; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7590</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7590"/>
		<updated>2026-09-30T11:05:35Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont, limit) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: limit, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                // The API answers 200 with an {error:...} body, so a failed&lt;br /&gt;
                // query is not a rejected promise. Treat it as one.&lt;br /&gt;
                if (j &amp;amp;&amp;amp; j.error) throw new Error(j.error.code || &#039;api error&#039;);&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) {&lt;br /&gt;
                    return page(j[&#039;continue&#039;].rccontinue, limit);&lt;br /&gt;
                }&lt;br /&gt;
                return all;&lt;br /&gt;
            }).catch(function (err) {&lt;br /&gt;
                // recentchanges over a date range is the most expensive query&lt;br /&gt;
                // this site makes, and on a busy or constrained database it&lt;br /&gt;
                // can come back as a DBQueryError. Halve the window and retry&lt;br /&gt;
                // once; failing that, return nothing. A &amp;quot;what&#039;s new&amp;quot; list is&lt;br /&gt;
                // not worth breaking the page for.&lt;br /&gt;
                if (limit &amp;gt; 100) return page(cont, 100);&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[gr] recent changes unavailable: &#039; +&lt;br /&gt;
                                 (err &amp;amp;&amp;amp; err.message || err));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null, 500);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([&lt;br /&gt;
            fetchChanges().catch(function () { return []; }),&lt;br /&gt;
            fetchCatalogue().catch(function () { return null; })&lt;br /&gt;
        ]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // ---- CONFIGURE ME -------------------------------------------------&lt;br /&gt;
    // Where the Grantha library&#039;s REST API lives. Same-origin by default,&lt;br /&gt;
    // which needs no CORS - but the library is its own app, so unless it is&lt;br /&gt;
    // reverse-proxied under the wiki&#039;s domain this must be its real host:&lt;br /&gt;
    //     var API_BASE = &#039;https://library.example.org/api/granthas&#039;;&lt;br /&gt;
    // On another host the library must send&lt;br /&gt;
    //     Access-Control-Allow-Origin: https://anandamakaranda.in&lt;br /&gt;
    // or the browser will refuse the reply and every citation will fall&lt;br /&gt;
    // back to the author baked into the page (usually none).&lt;br /&gt;
    var API_BASE = window.GR_LIBRARY_API || &#039;/api/granthas&#039;;&lt;br /&gt;
    // -------------------------------------------------------------------&lt;br /&gt;
    var API = API_BASE.replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var TTL = 6 * 60 * 60 * 1000;                 // names change rarely&lt;br /&gt;
    var mem = {};&lt;br /&gt;
    var warned = false;&lt;br /&gt;
&lt;br /&gt;
    function cacheGet(id) {&lt;br /&gt;
        if (mem[id]) return mem[id];&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:&#039; + id);&lt;br /&gt;
            if (!raw) return null;&lt;br /&gt;
            var o = JSON.parse(raw);&lt;br /&gt;
            if (!o || (Date.now() - o.t) &amp;gt; TTL) return null;&lt;br /&gt;
            mem[id] = o.v;&lt;br /&gt;
            return o.v;&lt;br /&gt;
        } catch (e) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function cacheSet(id, v) {&lt;br /&gt;
        mem[id] = v;&lt;br /&gt;
        try { sessionStorage.setItem(&#039;gr-lib:&#039; + id, JSON.stringify({ t: Date.now(), v: v })); }&lt;br /&gt;
        catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function (err) {&lt;br /&gt;
            // Offline, blocked by CORS, wrong host, or the id is not in the&lt;br /&gt;
            // library yet. The page&#039;s own data-author/data-doc-title stand&lt;br /&gt;
            // in, so a citation is never blocked on this request - but an&lt;br /&gt;
            // unreachable library looks exactly like a work with no author,&lt;br /&gt;
            // so say which it is, once.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            if (!warned) {&lt;br /&gt;
                warned = true;&lt;br /&gt;
                try {&lt;br /&gt;
                    console.warn(&#039;[grantha] could not read the library at &#039; + API +&lt;br /&gt;
                        &#039; (asked for &amp;quot;&#039; + id + &#039;&amp;quot;): &#039; + (err &amp;amp;&amp;amp; err.message || err) +&lt;br /&gt;
                        &#039;. Citations will use whatever the page itself carries. &#039; +&lt;br /&gt;
                        &#039;Set window.GR_LIBRARY_API to the library host, and allow &#039; +&lt;br /&gt;
                        &#039;this origin via Access-Control-Allow-Origin.&#039;);&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            }&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:map&#039;);&lt;br /&gt;
            var o = raw &amp;amp;&amp;amp; JSON.parse(raw);&lt;br /&gt;
            if (o &amp;amp;&amp;amp; (Date.now() - o.t) &amp;lt; TTL) cached = o.v;&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        if (cached) { mapPromise = Promise.resolve(cached); return mapPromise; }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages:{...},&lt;br /&gt;
                // locators:{...}} when division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m, locators: {} };&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try { sessionStorage.setItem(&#039;gr-lib:map&#039;, JSON.stringify({ t: Date.now(), v: m })); }&lt;br /&gt;
                catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) { return r.ok ? r.json() : null; })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        var missing = want.filter(function (i) { return !cacheGet(i); });&lt;br /&gt;
        if (missing.length &amp;gt; 5) {&lt;br /&gt;
            return catalogue().then(function (by) {&lt;br /&gt;
                var out = {};&lt;br /&gt;
                want.forEach(function (i) { out[i] = by[i] || cacheGet(i) || null; });&lt;br /&gt;
                return out;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7589</id>
		<title>MediaWiki:Gadget-GrAnnotations.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7589"/>
		<updated>2026-09-30T11:04:53Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── SHAREABLE CITATION ────────────────────────────────────────────&lt;br /&gt;
     A bookmark is personal, but what it produces is not: a reference in&lt;br /&gt;
     APA form plus a link that resolves for anyone.&lt;br /&gt;
&lt;br /&gt;
     The link cannot point at the bookmark itself. A bookmark wraps the&lt;br /&gt;
     reader&#039;s own selection in a span with an id generated in their&lt;br /&gt;
     browser; that span is never saved to the page, so #&amp;lt;bookmark-id&amp;gt;&lt;br /&gt;
     resolves for nobody else. The citation therefore anchors to the&lt;br /&gt;
     nearest enclosing block that the PUBLISHED page carries an id for -&lt;br /&gt;
     the same ids copyId.js links to, and the same ones cross-references&lt;br /&gt;
     across the site already use.&lt;br /&gt;
&lt;br /&gt;
     APA 7th, for a work on a website with no date:&lt;br /&gt;
       Author. (n.d.). Title (Locator). Site. Retrieved D Month Y, from URL&lt;br /&gt;
     n.d. because these works carry no publication date, and a retrieval&lt;br /&gt;
     date because a wiki page can change. The block reference is given as&lt;br /&gt;
     a parenthetical locator after the title, which is where APA puts a&lt;br /&gt;
     section or part designation. */&lt;br /&gt;
&lt;br /&gt;
  var CITE_BLOCK_SEL = &#039;.verse-block[id], .bhashyam-block[id], .teeka-block[id], &#039; +&lt;br /&gt;
                       &#039;[data-block-id][id], .gr-apparatus[id], .shloka[id]&#039;;&lt;br /&gt;
&lt;br /&gt;
  function citeAnchorFor(node) {&lt;br /&gt;
    var el = node &amp;amp;&amp;amp; node.nodeType === 3 ? node.parentNode : node;&lt;br /&gt;
    while (el &amp;amp;&amp;amp; el !== document.body) {&lt;br /&gt;
      if (el.matches &amp;amp;&amp;amp; el.matches(CITE_BLOCK_SEL)) return el;&lt;br /&gt;
      el = el.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Walks up from the cited block: an interleaved commentary block states&lt;br /&gt;
  // its own work, otherwise the page&#039;s title element does.&lt;br /&gt;
  // A reference written in English cites the work in IAST, so prefer the&lt;br /&gt;
  // transliterated forms the library supplies and keep Devanagari as the&lt;br /&gt;
  // fallback. common.js writes the library&#039;s values onto .gr-doc-title as&lt;br /&gt;
  // soon as they arrive, so reading the DOM here always gets the current&lt;br /&gt;
  // names without this module knowing the library exists.&lt;br /&gt;
  function citeSourceFor(el) {&lt;br /&gt;
    var n = el;&lt;br /&gt;
    while (n &amp;amp;&amp;amp; n !== document.body) {&lt;br /&gt;
      if (n.getAttribute &amp;amp;&amp;amp; n.getAttribute(&#039;data-cite-title&#039;)) {&lt;br /&gt;
        return {&lt;br /&gt;
          author: n.getAttribute(&#039;data-cite-author-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-author&#039;) || &#039;&#039;,&lt;br /&gt;
          title:  n.getAttribute(&#039;data-cite-title-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-title&#039;) || &#039;&#039;,&lt;br /&gt;
          page:   n.getAttribute(&#039;data-cite-page&#039;) || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      n = n.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return {&lt;br /&gt;
      author: (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-author-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-author&#039;))) || &#039;&#039;,&lt;br /&gt;
      title:  (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-doc-title-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-doc-title&#039;) ||&lt;br /&gt;
                     t.textContent.trim())) || document.title,&lt;br /&gt;
      page:   (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;,&lt;br /&gt;
      scheme: (t &amp;amp;&amp;amp; t.getAttribute(&#039;data-locator-scheme&#039;)) || &#039;&#039;&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Any block carrying a library id gets its author/title filled in from&lt;br /&gt;
  // the library, whether common.js injected it or the page was built with&lt;br /&gt;
  // it already in place. Doing it here rather than only at injection time&lt;br /&gt;
  // means a citation never depends on which path put the block on screen.&lt;br /&gt;
  function hydrateCitationSources(root) {&lt;br /&gt;
    if (!window.grLibrary) return;&lt;br /&gt;
    var nodes = (root || document).querySelectorAll(&#039;[data-cite-grantha-id]&#039;);&lt;br /&gt;
    Array.prototype.forEach.call(nodes, function (el) {&lt;br /&gt;
      if (el.getAttribute(&#039;data-cite-hydrated&#039;)) return;&lt;br /&gt;
      var gid = el.getAttribute(&#039;data-cite-grantha-id&#039;);&lt;br /&gt;
      if (!gid) return;&lt;br /&gt;
      el.setAttribute(&#039;data-cite-hydrated&#039;, &#039;1&#039;);&lt;br /&gt;
      window.grLibrary.lookup(gid).then(function (g) {&lt;br /&gt;
        if (!g) return;&lt;br /&gt;
        if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
        if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
        if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
        if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ── LOCATOR ─────────────────────────────────────────────────────────&lt;br /&gt;
  // &amp;quot;KKN_C01_S01_V03&amp;quot; is a build artefact, not a reference. A reader&lt;br /&gt;
  // citing it wants 1.1.3, and ideally to be told that those numbers are&lt;br /&gt;
  // adhyaya, valli and mantra. The page declares the division names it&lt;br /&gt;
  // knows (data-locator-scheme); where it declares none we still give the&lt;br /&gt;
  // dotted form, which is correct for every text - it is only the NAMES&lt;br /&gt;
  // of the divisions that vary from work to work.&lt;br /&gt;
  function parseScheme(s) {&lt;br /&gt;
    var out = {};&lt;br /&gt;
    (s || &#039;&#039;).split(&#039;,&#039;).forEach(function (pair) {&lt;br /&gt;
      var i = pair.indexOf(&#039;=&#039;);&lt;br /&gt;
      if (i &amp;gt; 0) out[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();&lt;br /&gt;
    });&lt;br /&gt;
    return out;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Segments that qualify the passage rather than number it: _B01 is the&lt;br /&gt;
  // first paragraph of the bhashya ON that verse, and a bare letter with&lt;br /&gt;
  // no number (Katha&#039;s &amp;quot;_I_&amp;quot; for an introduction) marks a position, not a&lt;br /&gt;
  // division. A NUMBERED segment is always part of the path - in&lt;br /&gt;
  // RG_C01_I01_V01 the I is a sukta, and reading it as Katha&#039;s&lt;br /&gt;
  // introduction marker turned 1.1.1 into &amp;quot;1.1, Sukta 1&amp;quot;.&lt;br /&gt;
  function isQualifier(k, n) { return k === &#039;B&#039; || !n; }&lt;br /&gt;
&lt;br /&gt;
  function parseLocator(id) {&lt;br /&gt;
    if (!id) return null;&lt;br /&gt;
    var segs = String(id).split(&#039;_&#039;);&lt;br /&gt;
    var path = [], qual = [];&lt;br /&gt;
    for (var i = 1; i &amp;lt; segs.length; i++) {&lt;br /&gt;
      var m = /^([A-Za-z]+)(\d*)$/.exec(segs[i]);&lt;br /&gt;
      if (!m) continue;&lt;br /&gt;
      var k = m[1].toUpperCase(), n = m[2] ? String(parseInt(m[2], 10)) : &#039;&#039;;&lt;br /&gt;
      (isQualifier(k, n) ? qual : path).push({ k: k, n: n });&lt;br /&gt;
    }&lt;br /&gt;
    if (!path.length &amp;amp;&amp;amp; !qual.length) return null;&lt;br /&gt;
    return { path: path, qual: qual };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function locatorText(id, scheme) {&lt;br /&gt;
    var p = parseLocator(id);&lt;br /&gt;
    if (!p) return { numeric: id || &#039;&#039;, spelled: &#039;&#039; };&lt;br /&gt;
    var labels = parseScheme(scheme);&lt;br /&gt;
    var numeric = p.path.map(function (s) { return s.n; }).filter(Boolean).join(&#039;.&#039;);&lt;br /&gt;
    var spelledParts = p.path.map(function (s) {&lt;br /&gt;
      return labels[s.k] ? labels[s.k] + &#039; &#039; + s.n : null;&lt;br /&gt;
    });&lt;br /&gt;
    var spelled = spelledParts.indexOf(null) === -1 ? spelledParts.join(&#039;, &#039;) : &#039;&#039;;&lt;br /&gt;
    p.qual.forEach(function (s) {&lt;br /&gt;
      var lab = labels[s.k] ||&lt;br /&gt;
                (s.k === &#039;B&#039; ? &#039;bhāṣya&#039; : (s.k === &#039;I&#039; ? &#039;intro&#039; : s.k));&lt;br /&gt;
      var bit = lab + (s.n ? &#039; &#039; + s.n : &#039;&#039;);&lt;br /&gt;
      numeric += (numeric ? &#039;, &#039; : &#039;&#039;) + bit;&lt;br /&gt;
      if (spelled) spelled += &#039;, &#039; + bit;&lt;br /&gt;
    });&lt;br /&gt;
    return { numeric: numeric || id, spelled: spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Version stamp, so it is possible to tell from the browser console&lt;br /&gt;
  // which build of this gadget a page is actually running. Check with&lt;br /&gt;
  //   window.grAnnotationsVersion&lt;br /&gt;
  window.grAnnotationsVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
&lt;br /&gt;
  // A citation is worthless if its link doesn&#039;t open, so prefer URLs we&lt;br /&gt;
  // KNOW are good over ones we assemble.&lt;br /&gt;
  //   - Same page as the reader: use the address they&#039;re already at. A wiki&lt;br /&gt;
  //     whose served paths differ from wgArticlePath (or that sits behind a&lt;br /&gt;
  //     rewrite) would otherwise yield a link that 404s.&lt;br /&gt;
  //   - Another page (an injected teeka block cites its own subpage): use&lt;br /&gt;
  //     mw.util.getUrl, the same builder every working link on the wiki&lt;br /&gt;
  //     comes from. wgArticlePath is only the last resort.&lt;br /&gt;
  function citeUrl(pageName, anchorId) {&lt;br /&gt;
    var here = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
    var frag = anchorId ? &#039;#&#039; + encodeURIComponent(anchorId) : &#039;&#039;;&lt;br /&gt;
    var base;&lt;br /&gt;
    if (!pageName || String(pageName) === String(here)) {&lt;br /&gt;
      base = location.origin + location.pathname;&lt;br /&gt;
    } else if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) {&lt;br /&gt;
      base = location.origin + mw.util.getUrl(String(pageName));&lt;br /&gt;
    } else {&lt;br /&gt;
      var path = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
      base = location.origin +&lt;br /&gt;
             path.replace(&#039;$1&#039;, String(pageName).split(&#039;/&#039;).map(encodeURIComponent).join(&#039;/&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    return base + frag;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var CITE_MONTHS = [&#039;January&#039;,&#039;February&#039;,&#039;March&#039;,&#039;April&#039;,&#039;May&#039;,&#039;June&#039;,&lt;br /&gt;
                     &#039;July&#039;,&#039;August&#039;,&#039;September&#039;,&#039;October&#039;,&#039;November&#039;,&#039;December&#039;];&lt;br /&gt;
&lt;br /&gt;
  function retrievedToday() {&lt;br /&gt;
    var d = new Date();&lt;br /&gt;
    return CITE_MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function siteName() {&lt;br /&gt;
    return (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgSiteName&#039;)) || location.hostname;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // APA 7th, webpage on a website (§10.16):&lt;br /&gt;
  //     Author. (n.d.). Title of work. Site Name. Retrieved &amp;lt;date&amp;gt;, from &amp;lt;URL&amp;gt;&lt;br /&gt;
  // Two rules this has to honour and an earlier draft did not:&lt;br /&gt;
  //   1. No known author -&amp;gt; the TITLE moves into the author position; you&lt;br /&gt;
  //      never leave a bare &amp;quot;(n.d.)&amp;quot; with nothing in front of it. And when&lt;br /&gt;
  //      the title occupies that slot the site name is not repeated after it.&lt;br /&gt;
  //   2. A locator (our block id) does NOT belong in the reference entry.&lt;br /&gt;
  //      The parenthetical slot after a title is for edition/format&lt;br /&gt;
  //      descriptors, not section numbers. APA puts locators in the in-text&lt;br /&gt;
  //      citation, so the block id goes there: (Author, n.d., IS_C01_I03).&lt;br /&gt;
  // The retrieval date is correct here because the page is designed to&lt;br /&gt;
  // change and we link to the live version rather than a permalink.&lt;br /&gt;
  // Titles of standalone works are italicised, which only the HTML flavour&lt;br /&gt;
  // of the clipboard can carry.&lt;br /&gt;
  function buildCitation(rec) {&lt;br /&gt;
    var src    = rec.citeSource || {};&lt;br /&gt;
    var author = (src.author || &#039;&#039;).trim();&lt;br /&gt;
    var title  = (src.title  || &#039;&#039;).trim();&lt;br /&gt;
    var anchor = rec.anchor || &#039;&#039;;&lt;br /&gt;
    var site   = (siteName() || &#039;&#039;).trim();&lt;br /&gt;
    var url    = citeUrl(src.page, anchor);&lt;br /&gt;
    var retrieved = retrievedToday();&lt;br /&gt;
    var loc    = locatorText(anchor, src.scheme);&lt;br /&gt;
&lt;br /&gt;
    var head, headHtml, showSite;&lt;br /&gt;
    if (author) {&lt;br /&gt;
      head = author + &#039;. (n.d.). &#039; + title + &#039;.&#039;;&lt;br /&gt;
      headHtml = esc(author) + &#039;. (n.d.). &amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;.&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== author;&lt;br /&gt;
    } else {&lt;br /&gt;
      // Title in the author position - italicised there too, and the site&lt;br /&gt;
      // name is dropped when it would only repeat what the title says.&lt;br /&gt;
      head = title + &#039;. (n.d.).&#039;;&lt;br /&gt;
      headHtml = &#039;&amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;. (n.d.).&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== title;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var tail = &#039; Retrieved &#039; + retrieved + &#039;, from &#039; + url;&lt;br /&gt;
    var reference = head + (showSite ? &#039; &#039; + site + &#039;.&#039; : &#039;&#039;) + tail;&lt;br /&gt;
    var referenceHtml = headHtml + (showSite ? &#039; &#039; + esc(site) + &#039;.&#039; : &#039;&#039;)&lt;br /&gt;
        + &#039; Retrieved &#039; + esc(retrieved) + &#039;, from &#039;&lt;br /&gt;
        + &#039;&amp;lt;a href=&amp;quot;&#039; + esc(url) + &#039;&amp;quot;&amp;gt;&#039; + esc(url) + &#039;&amp;lt;/a&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
    // The in-text citation carries the locator, expanded: 1.1.3, not&lt;br /&gt;
    // KKN_C01_S01_V03. The name is shortened the way an in-text citation&lt;br /&gt;
    // shortens one - the library&#039;s parenthetical alias (&amp;quot;Śrīman&lt;br /&gt;
    // Madhvācārya (Ānandatīrtha)&amp;quot;) belongs in the reference, not here.&lt;br /&gt;
    var shortName = (author || title).replace(/\s*\([^)]*\)\s*$/, &#039;&#039;).trim();&lt;br /&gt;
    var inText = &#039;(&#039; + (shortName || author || title) + &#039;, n.d.&#039;&lt;br /&gt;
               + (loc.numeric ? &#039;, &#039; + loc.numeric : &#039;&#039;) + &#039;)&#039;;&lt;br /&gt;
&lt;br /&gt;
    return { reference: reference, referenceHtml: referenceHtml,&lt;br /&gt;
             inText: inText, url: url,&lt;br /&gt;
             locator: loc.numeric, locatorSpelled: loc.spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // html is optional. When given, both flavours go on the clipboard so a&lt;br /&gt;
  // paste into Word/Docs keeps the italicised title and the live link,&lt;br /&gt;
  // while a paste into a plain field (or the address bar) gets the text.&lt;br /&gt;
  function copyToClipboard(text, done, html) {&lt;br /&gt;
    if (html &amp;amp;&amp;amp; window.ClipboardItem &amp;amp;&amp;amp; navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.write) {&lt;br /&gt;
      try {&lt;br /&gt;
        var item = new ClipboardItem({&lt;br /&gt;
          &#039;text/html&#039;:  new Blob([html], { type: &#039;text/html&#039; }),&lt;br /&gt;
          &#039;text/plain&#039;: new Blob([text], { type: &#039;text/plain&#039; })&lt;br /&gt;
        });&lt;br /&gt;
        navigator.clipboard.write([item]).then(done).catch(function () {&lt;br /&gt;
          plainCopy(text, done);&lt;br /&gt;
        });&lt;br /&gt;
        return;&lt;br /&gt;
      } catch (e) { /* fall through */ }&lt;br /&gt;
    }&lt;br /&gt;
    plainCopy(text, done);&lt;br /&gt;
  }&lt;br /&gt;
  function plainCopy(text, done) {&lt;br /&gt;
    if (navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.writeText) {&lt;br /&gt;
      navigator.clipboard.writeText(text).then(done).catch(function () { legacyCopy(text, done); });&lt;br /&gt;
    } else { legacyCopy(text, done); }&lt;br /&gt;
  }&lt;br /&gt;
  function legacyCopy(text, done) {&lt;br /&gt;
    var ta = document.createElement(&#039;textarea&#039;);&lt;br /&gt;
    ta.value = text;&lt;br /&gt;
    ta.style.cssText = &#039;position:fixed;top:-9999px;left:-9999px;&#039;;&lt;br /&gt;
    document.body.appendChild(ta); ta.focus(); ta.select();&lt;br /&gt;
    try { document.execCommand(&#039;copy&#039;); done &amp;amp;&amp;amp; done(); } catch (e) {}&lt;br /&gt;
    document.body.removeChild(ta);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    // Resolve the citation anchor BEFORE wrapping: wrapSelection inserts a&lt;br /&gt;
    // span of its own, and resolving afterwards would just find that.&lt;br /&gt;
    var anchorEl = citeAnchorFor(_selRange &amp;amp;&amp;amp; _selRange.startContainer);&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    if (!anchorEl &amp;amp;&amp;amp; span) anchorEl = citeAnchorFor(span);&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso(),&lt;br /&gt;
               anchor: anchorEl ? (anchorEl.id || anchorEl.getAttribute(&#039;data-block-id&#039;) || &#039;&#039;) : &#039;&#039;,&lt;br /&gt;
               citeSource: citeSourceFor(anchorEl || document.body)};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      var cite = buildCitation(b);&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            // One button. The reference already carries the work, the&lt;br /&gt;
            // locator&#039;s page and the link, so a separate &amp;quot;copy link&amp;quot; only&lt;br /&gt;
            // asked the reader to decide something they don&#039;t need to.&lt;br /&gt;
            // The in-text form stays on the card because it is what goes&lt;br /&gt;
            // in a sentence, but it copies on click rather than earning a&lt;br /&gt;
            // button of its own.&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-cite&amp;quot;&amp;gt;&#039;+cite.referenceHtml+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-intext&amp;quot; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&#039;&lt;br /&gt;
            + &#039; title=&amp;quot;Click to copy the in-text citation&amp;quot;&amp;gt;In text: &#039;+esc(cite.inText)&lt;br /&gt;
            + (cite.locatorSpelled ? &#039; &amp;lt;span class=&amp;quot;gra-cite-locator&amp;quot;&amp;gt;(&#039;&lt;br /&gt;
                 + esc(cite.locatorSpelled) + &#039;)&amp;lt;/span&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-cite-actions&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-what=&amp;quot;reference&amp;quot;&#039;&lt;br /&gt;
            + &#039; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;Copy citation&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    // CHANGED: grouped by source. A moola page now also carries the notes&lt;br /&gt;
    // of every commentary shown on it, and flattening them into one list&lt;br /&gt;
    // put a commentary&#039;s notes after the moola&#039;s with nothing saying which&lt;br /&gt;
    // was which - the cards were indistinguishable. Each note is filed&lt;br /&gt;
    // under the heading of the document it came from: the page&#039;s own notes&lt;br /&gt;
    // first, then one section per commentary, in the order the page shows&lt;br /&gt;
    // them. common.js wraps injected notes in .gr-teeka-notes-group with a&lt;br /&gt;
    // .gr-teeka-notes-title, which is where the heading comes from.&lt;br /&gt;
    function groupOf(li) {&lt;br /&gt;
      var wrap = li.closest ? li.closest(&#039;.gr-teeka-notes-group&#039;) : null;&lt;br /&gt;
      if (!wrap) return null;&lt;br /&gt;
      var t = wrap.querySelector(&#039;.gr-teeka-notes-title&#039;);&lt;br /&gt;
      return {&lt;br /&gt;
        slug: wrap.getAttribute(&#039;data-teeka-slug&#039;) || &#039;&#039;,&lt;br /&gt;
        title: t ? t.textContent.trim() : &#039;&#039;&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var sections = [];          // [{key, title, items: []}] in document order&lt;br /&gt;
    var byKey = {};&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var g = groupOf(li);&lt;br /&gt;
      var key = g ? g.slug : &#039;&#039;;&lt;br /&gt;
      if (!byKey[key]) {&lt;br /&gt;
        byKey[key] = { key: key, title: g ? g.title : &#039;&#039;, items: [] };&lt;br /&gt;
        sections.push(byKey[key]);&lt;br /&gt;
      }&lt;br /&gt;
      byKey[key].items.push(li);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    sections.forEach(function (sec) {&lt;br /&gt;
      if (sec.title) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title&amp;quot;&amp;gt;&#039; + esc(sec.title) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      } else if (sections.length &amp;gt; 1) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title gra-footnote-section-own&amp;quot;&amp;gt;&#039;&lt;br /&gt;
              + esc(pageOwnNotesLabel()) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      }&lt;br /&gt;
      sec.items.forEach(function (li) { html += footnoteCard(li, sec.title); });&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The heading for the page&#039;s own notes, shown only when there is at&lt;br /&gt;
  // least one commentary section to tell them apart from.&lt;br /&gt;
  function pageOwnNotesLabel() {&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return (t &amp;amp;&amp;amp; t.textContent.trim()) || &#039;This page&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function footnoteCard(li, groupTitle) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      // The commentary&#039;s name is repeated on the card itself, not only on&lt;br /&gt;
      // the section heading: a reader who has scrolled the panel, or who&lt;br /&gt;
      // arrives at a card by clicking a word in the text, sees the card&lt;br /&gt;
      // without its heading in view.&lt;br /&gt;
      return &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (groupTitle ? &#039;&amp;lt;div class=&amp;quot;gra-footnote-source&amp;quot;&amp;gt;&#039;+esc(groupTitle)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-cite-copy&#039;)) return;&lt;br /&gt;
      if ($(e.target).closest(&#039;.gra-bookmark-intext&#039;).length) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-intext&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $el = $(this), id = $el.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      copyToClipboard(buildCitation(rec).inText, function(){&lt;br /&gt;
        $el.addClass(&#039;gra-intext--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $el.removeClass(&#039;gra-intext--done&#039;); }, 1200);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-cite-copy&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $btn = $(this), id = $btn.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      var cite = buildCitation(rec);&lt;br /&gt;
      var what = $btn.attr(&#039;data-cite-what&#039;) || &#039;reference&#039;;&lt;br /&gt;
      // Only the reference carries formatting worth preserving; the in-text&lt;br /&gt;
      // form and the bare link must stay plain so they survive a paste into&lt;br /&gt;
      // the address bar.&lt;br /&gt;
      var text = cite[what] || cite.reference;&lt;br /&gt;
      var html = (what === &#039;reference&#039;) ? cite.referenceHtml : null;&lt;br /&gt;
      var was = $btn.text();&lt;br /&gt;
      copyToClipboard(text, function(){&lt;br /&gt;
        $btn.text(&#039;Copied&#039;).addClass(&#039;gra-cite-copy--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $btn.text(was).removeClass(&#039;gra-cite-copy--done&#039;); }, 1600);&lt;br /&gt;
      }, html);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
    hydrateCitationSources();&lt;br /&gt;
    // Commentary blocks arrive after the page does, so hydrate again when&lt;br /&gt;
    // they land rather than only once at boot.&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
      hydrateCitationSources(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  // The count badge is gone. It sat on the round toggle, which clips its&lt;br /&gt;
  // own overflow to stay circular, so the badge was cut off on every side&lt;br /&gt;
  // and told the reader nothing they could act on. Kept as a no-op so the&lt;br /&gt;
  // five call sites that report a change still read clearly.&lt;br /&gt;
  function updateBadge() { }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:GranthaMap.json&amp;diff=7588</id>
		<title>MediaWiki:GranthaMap.json</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:GranthaMap.json&amp;diff=7588"/>
		<updated>2026-09-30T09:25:29Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
	&amp;quot;pages&amp;quot;: {&lt;br /&gt;
		&amp;quot;Aitareya&amp;quot;: &amp;quot;UP.08.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Anubhashya&amp;quot;: &amp;quot;SP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Anuvyakhyana&amp;quot;: &amp;quot;SP.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Anuvyakhyana/Vyakhya/Nyayasudha&amp;quot;: &amp;quot;SP.01.03.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavadgitabhashya&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavadgitatatparya&amp;quot;: &amp;quot;GP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavadgitatatparya/Vyakhya/Nyayadeepika&amp;quot;: &amp;quot;GP.01.02.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Bhagavatatatparyanirnaya&amp;quot;: &amp;quot;IP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Brahmasutra&amp;quot;: &amp;quot;SP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Bruhadaranyaka&amp;quot;: &amp;quot;UP.10.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.09.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/VedeshaPadarthakaumudi&amp;quot;: &amp;quot;UP.09.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Dwadasha&amp;quot;: &amp;quot;SA.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.01.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/NrsimhacharyaDurgamavakyarthavivarana&amp;quot;: &amp;quot;UP.01.01.02.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/RaghunathaVivriti&amp;quot;: &amp;quot;UP.01.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/SrinivasaVyakhyana&amp;quot;: &amp;quot;UP.01.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.01.01.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/JayatirthaPrameyadipika&amp;quot;: &amp;quot;GP.01.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/SumatindraBhavaratnakosha&amp;quot;: &amp;quot;GP.01.01.03.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Isha&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Jayantinirnaya&amp;quot;: &amp;quot;SA.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Kandukastuti&amp;quot;: &amp;quot;SA.08&amp;quot;,&lt;br /&gt;
		&amp;quot;Karmanirnaya&amp;quot;: &amp;quot;DP.10&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha&amp;quot;: &amp;quot;UP.03.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.03.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.03.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VaradatirthaCommentary&amp;quot;: &amp;quot;UP.03.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VedeshatirthaVivarana&amp;quot;: &amp;quot;UP.03.01.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot;: &amp;quot;UP.03.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Kathalakshanam&amp;quot;: &amp;quot;DP.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Kathalakshanam/Vyakhya/Kathalakshana-tika&amp;quot;: &amp;quot;DP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Kathalakshanam/Vyakhya/Kathalakshanapanchika&amp;quot;: &amp;quot;DP.02.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Krishnamrutamaharnava&amp;quot;: &amp;quot;SA.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Mahabharatatatparyanirnaya&amp;quot;: &amp;quot;IP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Mahaitareya&amp;quot;: &amp;quot;UP.08.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Mandu&amp;quot;: &amp;quot;UP.06.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Mayavadakhandanam&amp;quot;: &amp;quot;DP.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Mithyatvanumanakhanadanam&amp;quot;: &amp;quot;DP.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Mundaka&amp;quot;: &amp;quot;UP.05.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Nakha&amp;quot;: &amp;quot;SA.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Nakha/Vyakhya/BalabodhiniNakha&amp;quot;: &amp;quot;SA.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Nakha/Vyakhya/MandabodhiniNakha&amp;quot;: &amp;quot;SA.01.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Nyayavivaranam&amp;quot;: &amp;quot;SP.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Padyamala&amp;quot;: &amp;quot;RC.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Pramaanalakshanam&amp;quot;: &amp;quot;DP.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Pramaanalakshanam/Vyakhya/Pramaanalakshana-tika&amp;quot;: &amp;quot;DP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Pramanapaddati&amp;quot;: &amp;quot;RC.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Rigbhashyam&amp;quot;: &amp;quot;RP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Sadacharasmriti&amp;quot;: &amp;quot;SA.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Satprashna&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.04.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.04.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/SrinivasaTippani&amp;quot;: &amp;quot;UP.04.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.04.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Taittiriya&amp;quot;: &amp;quot;UP.07.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara&amp;quot;: &amp;quot;UP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.02.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VaradaTika&amp;quot;: &amp;quot;UP.02.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VedeshaVivarana&amp;quot;: &amp;quot;UP.02.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VyasatirthaVivarana&amp;quot;: &amp;quot;UP.02.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/GudhakartrikaTippani&amp;quot;: &amp;quot;SA.05.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/MangaliTantrasarasangrahatippani&amp;quot;: &amp;quot;SA.05.10&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/RaghunathaTattvakanika&amp;quot;: &amp;quot;SA.05.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VasudhendraTantrasarasangrahavivarana&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VedavedyaTantrasaravivriti&amp;quot;: &amp;quot;SA.05.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VedavyasaPadabhavaprabodhini&amp;quot;: &amp;quot;SA.05.12&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VenkatanarasimhacharyaTantrasaravivriti&amp;quot;: &amp;quot;SA.05.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VenkatapatyacharyaGurvarthadidhiti&amp;quot;: &amp;quot;SA.05.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasarasangraha&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvasankhyanam&amp;quot;: &amp;quot;DP.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvasankhyanam/Vyakhya/Tattvasankhyanam-tika&amp;quot;: &amp;quot;DP.06.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvaviveka&amp;quot;: &amp;quot;DP.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvaviveka/Vyakhya/Tatvaviveka-tika&amp;quot;: &amp;quot;DP.07.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Tattvodyota&amp;quot;: &amp;quot;DP.08&amp;quot;,&lt;br /&gt;
		&amp;quot;Upadhikhandanam&amp;quot;: &amp;quot;DP.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali&amp;quot;: &amp;quot;RC.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali/Vyakhya/Bhavadipika&amp;quot;: &amp;quot;RC.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali/Vyakhya/Bhavaprakashaha&amp;quot;: &amp;quot;RC.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Vadavali/Vyakhya/Bhavavivaranam&amp;quot;: &amp;quot;RC.02.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti&amp;quot;: &amp;quot;RC.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/BhagavantarayaLaghunigudharthaprakashika&amp;quot;: &amp;quot;RC.09.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/KurmaBhavapradipika&amp;quot;: &amp;quot;RC.09.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/SrinivasaShrinivasatirthiya&amp;quot;: &amp;quot;RC.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/VedatmaKavikarnamrita&amp;quot;: &amp;quot;RC.09.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/VishwapatiBalabodhini&amp;quot;: &amp;quot;RC.09.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Vishnutattvavinirnaya&amp;quot;: &amp;quot;DP.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Yamakabharatam&amp;quot;: &amp;quot;IP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Yamakabharatam/Vyakhya/YamakabharataTikaYadupatyam&amp;quot;: &amp;quot;IP.01.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Yatipranavavakalpa&amp;quot;: &amp;quot;SA.06&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;locators&amp;quot;: {&lt;br /&gt;
		&amp;quot;Katha&amp;quot;: &amp;quot;C=Adhyāya,S=Vallī,V=Mantra,B=Bhāṣya&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7587</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7587"/>
		<updated>2026-09-30T09:23:28Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // Same-origin by default so no CORS is needed. Override before this&lt;br /&gt;
    // file loads if the library app lives on another host.&lt;br /&gt;
    var API = (window.GR_LIBRARY_API || &#039;/api/granthas&#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var TTL = 6 * 60 * 60 * 1000;                 // names change rarely&lt;br /&gt;
    var mem = {};&lt;br /&gt;
&lt;br /&gt;
    function cacheGet(id) {&lt;br /&gt;
        if (mem[id]) return mem[id];&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:&#039; + id);&lt;br /&gt;
            if (!raw) return null;&lt;br /&gt;
            var o = JSON.parse(raw);&lt;br /&gt;
            if (!o || (Date.now() - o.t) &amp;gt; TTL) return null;&lt;br /&gt;
            mem[id] = o.v;&lt;br /&gt;
            return o.v;&lt;br /&gt;
        } catch (e) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function cacheSet(id, v) {&lt;br /&gt;
        mem[id] = v;&lt;br /&gt;
        try { sessionStorage.setItem(&#039;gr-lib:&#039; + id, JSON.stringify({ t: Date.now(), v: v })); }&lt;br /&gt;
        catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function () {&lt;br /&gt;
            // Offline, blocked, or the id is not in the library yet. The&lt;br /&gt;
            // page&#039;s own data-author/data-doc-title stand in; a citation&lt;br /&gt;
            // is never blocked on this request.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:map&#039;);&lt;br /&gt;
            var o = raw &amp;amp;&amp;amp; JSON.parse(raw);&lt;br /&gt;
            if (o &amp;amp;&amp;amp; (Date.now() - o.t) &amp;lt; TTL) cached = o.v;&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        if (cached) { mapPromise = Promise.resolve(cached); return mapPromise; }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages:{...},&lt;br /&gt;
                // locators:{...}} when division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m, locators: {} };&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try { sessionStorage.setItem(&#039;gr-lib:map&#039;, JSON.stringify({ t: Date.now(), v: m })); }&lt;br /&gt;
                catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) { return r.ok ? r.json() : null; })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        var missing = want.filter(function (i) { return !cacheGet(i); });&lt;br /&gt;
        if (missing.length &amp;gt; 5) {&lt;br /&gt;
            return catalogue().then(function (by) {&lt;br /&gt;
                var out = {};&lt;br /&gt;
                want.forEach(function (i) { out[i] = by[i] || cacheGet(i) || null; });&lt;br /&gt;
                return out;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibraryVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7586</id>
		<title>MediaWiki:Gadget-GrAnnotations.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7586"/>
		<updated>2026-09-30T07:21:40Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span id=&amp;quot;gra-toggle-badge&amp;quot; aria-live=&amp;quot;polite&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── SHAREABLE CITATION ────────────────────────────────────────────&lt;br /&gt;
     A bookmark is personal, but what it produces is not: a reference in&lt;br /&gt;
     APA form plus a link that resolves for anyone.&lt;br /&gt;
&lt;br /&gt;
     The link cannot point at the bookmark itself. A bookmark wraps the&lt;br /&gt;
     reader&#039;s own selection in a span with an id generated in their&lt;br /&gt;
     browser; that span is never saved to the page, so #&amp;lt;bookmark-id&amp;gt;&lt;br /&gt;
     resolves for nobody else. The citation therefore anchors to the&lt;br /&gt;
     nearest enclosing block that the PUBLISHED page carries an id for -&lt;br /&gt;
     the same ids copyId.js links to, and the same ones cross-references&lt;br /&gt;
     across the site already use.&lt;br /&gt;
&lt;br /&gt;
     APA 7th, for a work on a website with no date:&lt;br /&gt;
       Author. (n.d.). Title (Locator). Site. Retrieved D Month Y, from URL&lt;br /&gt;
     n.d. because these works carry no publication date, and a retrieval&lt;br /&gt;
     date because a wiki page can change. The block reference is given as&lt;br /&gt;
     a parenthetical locator after the title, which is where APA puts a&lt;br /&gt;
     section or part designation. */&lt;br /&gt;
&lt;br /&gt;
  var CITE_BLOCK_SEL = &#039;.verse-block[id], .bhashyam-block[id], .teeka-block[id], &#039; +&lt;br /&gt;
                       &#039;[data-block-id][id], .gr-apparatus[id], .shloka[id]&#039;;&lt;br /&gt;
&lt;br /&gt;
  function citeAnchorFor(node) {&lt;br /&gt;
    var el = node &amp;amp;&amp;amp; node.nodeType === 3 ? node.parentNode : node;&lt;br /&gt;
    while (el &amp;amp;&amp;amp; el !== document.body) {&lt;br /&gt;
      if (el.matches &amp;amp;&amp;amp; el.matches(CITE_BLOCK_SEL)) return el;&lt;br /&gt;
      el = el.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Walks up from the cited block: an interleaved commentary block states&lt;br /&gt;
  // its own work, otherwise the page&#039;s title element does.&lt;br /&gt;
  // A reference written in English cites the work in IAST, so prefer the&lt;br /&gt;
  // transliterated forms the library supplies and keep Devanagari as the&lt;br /&gt;
  // fallback. common.js writes the library&#039;s values onto .gr-doc-title as&lt;br /&gt;
  // soon as they arrive, so reading the DOM here always gets the current&lt;br /&gt;
  // names without this module knowing the library exists.&lt;br /&gt;
  function citeSourceFor(el) {&lt;br /&gt;
    var n = el;&lt;br /&gt;
    while (n &amp;amp;&amp;amp; n !== document.body) {&lt;br /&gt;
      if (n.getAttribute &amp;amp;&amp;amp; n.getAttribute(&#039;data-cite-title&#039;)) {&lt;br /&gt;
        return {&lt;br /&gt;
          author: n.getAttribute(&#039;data-cite-author-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-author&#039;) || &#039;&#039;,&lt;br /&gt;
          title:  n.getAttribute(&#039;data-cite-title-iast&#039;) ||&lt;br /&gt;
                  n.getAttribute(&#039;data-cite-title&#039;) || &#039;&#039;,&lt;br /&gt;
          page:   n.getAttribute(&#039;data-cite-page&#039;) || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      n = n.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return {&lt;br /&gt;
      author: (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-author-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-author&#039;))) || &#039;&#039;,&lt;br /&gt;
      title:  (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-doc-title-iast&#039;) ||&lt;br /&gt;
                     t.getAttribute(&#039;data-doc-title&#039;) ||&lt;br /&gt;
                     t.textContent.trim())) || document.title,&lt;br /&gt;
      page:   (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;,&lt;br /&gt;
      scheme: (t &amp;amp;&amp;amp; t.getAttribute(&#039;data-locator-scheme&#039;)) || &#039;&#039;&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Any block carrying a library id gets its author/title filled in from&lt;br /&gt;
  // the library, whether common.js injected it or the page was built with&lt;br /&gt;
  // it already in place. Doing it here rather than only at injection time&lt;br /&gt;
  // means a citation never depends on which path put the block on screen.&lt;br /&gt;
  function hydrateCitationSources(root) {&lt;br /&gt;
    if (!window.grLibrary) return;&lt;br /&gt;
    var nodes = (root || document).querySelectorAll(&#039;[data-cite-grantha-id]&#039;);&lt;br /&gt;
    Array.prototype.forEach.call(nodes, function (el) {&lt;br /&gt;
      if (el.getAttribute(&#039;data-cite-hydrated&#039;)) return;&lt;br /&gt;
      var gid = el.getAttribute(&#039;data-cite-grantha-id&#039;);&lt;br /&gt;
      if (!gid) return;&lt;br /&gt;
      el.setAttribute(&#039;data-cite-hydrated&#039;, &#039;1&#039;);&lt;br /&gt;
      window.grLibrary.lookup(gid).then(function (g) {&lt;br /&gt;
        if (!g) return;&lt;br /&gt;
        if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
        if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
        if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
        if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // ── LOCATOR ─────────────────────────────────────────────────────────&lt;br /&gt;
  // &amp;quot;KKN_C01_S01_V03&amp;quot; is a build artefact, not a reference. A reader&lt;br /&gt;
  // citing it wants 1.1.3, and ideally to be told that those numbers are&lt;br /&gt;
  // adhyaya, valli and mantra. The page declares the division names it&lt;br /&gt;
  // knows (data-locator-scheme); where it declares none we still give the&lt;br /&gt;
  // dotted form, which is correct for every text - it is only the NAMES&lt;br /&gt;
  // of the divisions that vary from work to work.&lt;br /&gt;
  function parseScheme(s) {&lt;br /&gt;
    var out = {};&lt;br /&gt;
    (s || &#039;&#039;).split(&#039;,&#039;).forEach(function (pair) {&lt;br /&gt;
      var i = pair.indexOf(&#039;=&#039;);&lt;br /&gt;
      if (i &amp;gt; 0) out[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();&lt;br /&gt;
    });&lt;br /&gt;
    return out;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Segments that qualify the passage rather than number it: _B01 is the&lt;br /&gt;
  // first paragraph of the bhashya ON that verse, and a bare letter with&lt;br /&gt;
  // no number (Katha&#039;s &amp;quot;_I_&amp;quot; for an introduction) marks a position, not a&lt;br /&gt;
  // division. A NUMBERED segment is always part of the path - in&lt;br /&gt;
  // RG_C01_I01_V01 the I is a sukta, and reading it as Katha&#039;s&lt;br /&gt;
  // introduction marker turned 1.1.1 into &amp;quot;1.1, Sukta 1&amp;quot;.&lt;br /&gt;
  function isQualifier(k, n) { return k === &#039;B&#039; || !n; }&lt;br /&gt;
&lt;br /&gt;
  function parseLocator(id) {&lt;br /&gt;
    if (!id) return null;&lt;br /&gt;
    var segs = String(id).split(&#039;_&#039;);&lt;br /&gt;
    var path = [], qual = [];&lt;br /&gt;
    for (var i = 1; i &amp;lt; segs.length; i++) {&lt;br /&gt;
      var m = /^([A-Za-z]+)(\d*)$/.exec(segs[i]);&lt;br /&gt;
      if (!m) continue;&lt;br /&gt;
      var k = m[1].toUpperCase(), n = m[2] ? String(parseInt(m[2], 10)) : &#039;&#039;;&lt;br /&gt;
      (isQualifier(k, n) ? qual : path).push({ k: k, n: n });&lt;br /&gt;
    }&lt;br /&gt;
    if (!path.length &amp;amp;&amp;amp; !qual.length) return null;&lt;br /&gt;
    return { path: path, qual: qual };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function locatorText(id, scheme) {&lt;br /&gt;
    var p = parseLocator(id);&lt;br /&gt;
    if (!p) return { numeric: id || &#039;&#039;, spelled: &#039;&#039; };&lt;br /&gt;
    var labels = parseScheme(scheme);&lt;br /&gt;
    var numeric = p.path.map(function (s) { return s.n; }).filter(Boolean).join(&#039;.&#039;);&lt;br /&gt;
    var spelledParts = p.path.map(function (s) {&lt;br /&gt;
      return labels[s.k] ? labels[s.k] + &#039; &#039; + s.n : null;&lt;br /&gt;
    });&lt;br /&gt;
    var spelled = spelledParts.indexOf(null) === -1 ? spelledParts.join(&#039;, &#039;) : &#039;&#039;;&lt;br /&gt;
    p.qual.forEach(function (s) {&lt;br /&gt;
      var lab = labels[s.k] ||&lt;br /&gt;
                (s.k === &#039;B&#039; ? &#039;bhāṣya&#039; : (s.k === &#039;I&#039; ? &#039;intro&#039; : s.k));&lt;br /&gt;
      var bit = lab + (s.n ? &#039; &#039; + s.n : &#039;&#039;);&lt;br /&gt;
      numeric += (numeric ? &#039;, &#039; : &#039;&#039;) + bit;&lt;br /&gt;
      if (spelled) spelled += &#039;, &#039; + bit;&lt;br /&gt;
    });&lt;br /&gt;
    return { numeric: numeric || id, spelled: spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Version stamp, so it is possible to tell from the browser console&lt;br /&gt;
  // which build of this gadget a page is actually running. Check with&lt;br /&gt;
  //   window.grAnnotationsVersion&lt;br /&gt;
  window.grAnnotationsVersion = &#039;2026-09-30a&#039;;&lt;br /&gt;
&lt;br /&gt;
  // A citation is worthless if its link doesn&#039;t open, so prefer URLs we&lt;br /&gt;
  // KNOW are good over ones we assemble.&lt;br /&gt;
  //   - Same page as the reader: use the address they&#039;re already at. A wiki&lt;br /&gt;
  //     whose served paths differ from wgArticlePath (or that sits behind a&lt;br /&gt;
  //     rewrite) would otherwise yield a link that 404s.&lt;br /&gt;
  //   - Another page (an injected teeka block cites its own subpage): use&lt;br /&gt;
  //     mw.util.getUrl, the same builder every working link on the wiki&lt;br /&gt;
  //     comes from. wgArticlePath is only the last resort.&lt;br /&gt;
  function citeUrl(pageName, anchorId) {&lt;br /&gt;
    var here = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
    var frag = anchorId ? &#039;#&#039; + encodeURIComponent(anchorId) : &#039;&#039;;&lt;br /&gt;
    var base;&lt;br /&gt;
    if (!pageName || String(pageName) === String(here)) {&lt;br /&gt;
      base = location.origin + location.pathname;&lt;br /&gt;
    } else if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) {&lt;br /&gt;
      base = location.origin + mw.util.getUrl(String(pageName));&lt;br /&gt;
    } else {&lt;br /&gt;
      var path = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
      base = location.origin +&lt;br /&gt;
             path.replace(&#039;$1&#039;, String(pageName).split(&#039;/&#039;).map(encodeURIComponent).join(&#039;/&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    return base + frag;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var CITE_MONTHS = [&#039;January&#039;,&#039;February&#039;,&#039;March&#039;,&#039;April&#039;,&#039;May&#039;,&#039;June&#039;,&lt;br /&gt;
                     &#039;July&#039;,&#039;August&#039;,&#039;September&#039;,&#039;October&#039;,&#039;November&#039;,&#039;December&#039;];&lt;br /&gt;
&lt;br /&gt;
  function retrievedToday() {&lt;br /&gt;
    var d = new Date();&lt;br /&gt;
    return CITE_MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function siteName() {&lt;br /&gt;
    return (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgSiteName&#039;)) || location.hostname;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // APA 7th, webpage on a website (§10.16):&lt;br /&gt;
  //     Author. (n.d.). Title of work. Site Name. Retrieved &amp;lt;date&amp;gt;, from &amp;lt;URL&amp;gt;&lt;br /&gt;
  // Two rules this has to honour and an earlier draft did not:&lt;br /&gt;
  //   1. No known author -&amp;gt; the TITLE moves into the author position; you&lt;br /&gt;
  //      never leave a bare &amp;quot;(n.d.)&amp;quot; with nothing in front of it. And when&lt;br /&gt;
  //      the title occupies that slot the site name is not repeated after it.&lt;br /&gt;
  //   2. A locator (our block id) does NOT belong in the reference entry.&lt;br /&gt;
  //      The parenthetical slot after a title is for edition/format&lt;br /&gt;
  //      descriptors, not section numbers. APA puts locators in the in-text&lt;br /&gt;
  //      citation, so the block id goes there: (Author, n.d., IS_C01_I03).&lt;br /&gt;
  // The retrieval date is correct here because the page is designed to&lt;br /&gt;
  // change and we link to the live version rather than a permalink.&lt;br /&gt;
  // Titles of standalone works are italicised, which only the HTML flavour&lt;br /&gt;
  // of the clipboard can carry.&lt;br /&gt;
  function buildCitation(rec) {&lt;br /&gt;
    var src    = rec.citeSource || {};&lt;br /&gt;
    var author = (src.author || &#039;&#039;).trim();&lt;br /&gt;
    var title  = (src.title  || &#039;&#039;).trim();&lt;br /&gt;
    var anchor = rec.anchor || &#039;&#039;;&lt;br /&gt;
    var site   = (siteName() || &#039;&#039;).trim();&lt;br /&gt;
    var url    = citeUrl(src.page, anchor);&lt;br /&gt;
    var retrieved = retrievedToday();&lt;br /&gt;
    var loc    = locatorText(anchor, src.scheme);&lt;br /&gt;
&lt;br /&gt;
    var head, headHtml, showSite;&lt;br /&gt;
    if (author) {&lt;br /&gt;
      head = author + &#039;. (n.d.). &#039; + title + &#039;.&#039;;&lt;br /&gt;
      headHtml = esc(author) + &#039;. (n.d.). &amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;.&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== author;&lt;br /&gt;
    } else {&lt;br /&gt;
      // Title in the author position - italicised there too, and the site&lt;br /&gt;
      // name is dropped when it would only repeat what the title says.&lt;br /&gt;
      head = title + &#039;. (n.d.).&#039;;&lt;br /&gt;
      headHtml = &#039;&amp;lt;i&amp;gt;&#039; + esc(title) + &#039;&amp;lt;/i&amp;gt;. (n.d.).&#039;;&lt;br /&gt;
      showSite = site &amp;amp;&amp;amp; site !== title;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var tail = &#039; Retrieved &#039; + retrieved + &#039;, from &#039; + url;&lt;br /&gt;
    var reference = head + (showSite ? &#039; &#039; + site + &#039;.&#039; : &#039;&#039;) + tail;&lt;br /&gt;
    var referenceHtml = headHtml + (showSite ? &#039; &#039; + esc(site) + &#039;.&#039; : &#039;&#039;)&lt;br /&gt;
        + &#039; Retrieved &#039; + esc(retrieved) + &#039;, from &#039;&lt;br /&gt;
        + &#039;&amp;lt;a href=&amp;quot;&#039; + esc(url) + &#039;&amp;quot;&amp;gt;&#039; + esc(url) + &#039;&amp;lt;/a&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
    // The in-text citation carries the locator, expanded: 1.1.3, not&lt;br /&gt;
    // KKN_C01_S01_V03. The name is shortened the way an in-text citation&lt;br /&gt;
    // shortens one - the library&#039;s parenthetical alias (&amp;quot;Śrīman&lt;br /&gt;
    // Madhvācārya (Ānandatīrtha)&amp;quot;) belongs in the reference, not here.&lt;br /&gt;
    var shortName = (author || title).replace(/\s*\([^)]*\)\s*$/, &#039;&#039;).trim();&lt;br /&gt;
    var inText = &#039;(&#039; + (shortName || author || title) + &#039;, n.d.&#039;&lt;br /&gt;
               + (loc.numeric ? &#039;, &#039; + loc.numeric : &#039;&#039;) + &#039;)&#039;;&lt;br /&gt;
&lt;br /&gt;
    return { reference: reference, referenceHtml: referenceHtml,&lt;br /&gt;
             inText: inText, url: url,&lt;br /&gt;
             locator: loc.numeric, locatorSpelled: loc.spelled };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // html is optional. When given, both flavours go on the clipboard so a&lt;br /&gt;
  // paste into Word/Docs keeps the italicised title and the live link,&lt;br /&gt;
  // while a paste into a plain field (or the address bar) gets the text.&lt;br /&gt;
  function copyToClipboard(text, done, html) {&lt;br /&gt;
    if (html &amp;amp;&amp;amp; window.ClipboardItem &amp;amp;&amp;amp; navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.write) {&lt;br /&gt;
      try {&lt;br /&gt;
        var item = new ClipboardItem({&lt;br /&gt;
          &#039;text/html&#039;:  new Blob([html], { type: &#039;text/html&#039; }),&lt;br /&gt;
          &#039;text/plain&#039;: new Blob([text], { type: &#039;text/plain&#039; })&lt;br /&gt;
        });&lt;br /&gt;
        navigator.clipboard.write([item]).then(done).catch(function () {&lt;br /&gt;
          plainCopy(text, done);&lt;br /&gt;
        });&lt;br /&gt;
        return;&lt;br /&gt;
      } catch (e) { /* fall through */ }&lt;br /&gt;
    }&lt;br /&gt;
    plainCopy(text, done);&lt;br /&gt;
  }&lt;br /&gt;
  function plainCopy(text, done) {&lt;br /&gt;
    if (navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.writeText) {&lt;br /&gt;
      navigator.clipboard.writeText(text).then(done).catch(function () { legacyCopy(text, done); });&lt;br /&gt;
    } else { legacyCopy(text, done); }&lt;br /&gt;
  }&lt;br /&gt;
  function legacyCopy(text, done) {&lt;br /&gt;
    var ta = document.createElement(&#039;textarea&#039;);&lt;br /&gt;
    ta.value = text;&lt;br /&gt;
    ta.style.cssText = &#039;position:fixed;top:-9999px;left:-9999px;&#039;;&lt;br /&gt;
    document.body.appendChild(ta); ta.focus(); ta.select();&lt;br /&gt;
    try { document.execCommand(&#039;copy&#039;); done &amp;amp;&amp;amp; done(); } catch (e) {}&lt;br /&gt;
    document.body.removeChild(ta);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    // Resolve the citation anchor BEFORE wrapping: wrapSelection inserts a&lt;br /&gt;
    // span of its own, and resolving afterwards would just find that.&lt;br /&gt;
    var anchorEl = citeAnchorFor(_selRange &amp;amp;&amp;amp; _selRange.startContainer);&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    if (!anchorEl &amp;amp;&amp;amp; span) anchorEl = citeAnchorFor(span);&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso(),&lt;br /&gt;
               anchor: anchorEl ? (anchorEl.id || anchorEl.getAttribute(&#039;data-block-id&#039;) || &#039;&#039;) : &#039;&#039;,&lt;br /&gt;
               citeSource: citeSourceFor(anchorEl || document.body)};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      var cite = buildCitation(b);&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-cite&amp;quot;&amp;gt;&#039;+cite.referenceHtml+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-intext&amp;quot;&amp;gt;In text: &#039;+esc(cite.inText)&lt;br /&gt;
            + (cite.locatorSpelled ? &#039; &amp;lt;span class=&amp;quot;gra-cite-locator&amp;quot;&amp;gt;(&#039;&lt;br /&gt;
                 + esc(cite.locatorSpelled) + &#039;)&amp;lt;/span&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-cite-actions&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-what=&amp;quot;reference&amp;quot;&#039;&lt;br /&gt;
            + &#039; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;Copy reference&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-what=&amp;quot;inText&amp;quot;&#039;&lt;br /&gt;
            + &#039; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;Copy in-text&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-what=&amp;quot;url&amp;quot;&#039;&lt;br /&gt;
            + &#039; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;Copy link&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    // CHANGED: grouped by source. A moola page now also carries the notes&lt;br /&gt;
    // of every commentary shown on it, and flattening them into one list&lt;br /&gt;
    // put a commentary&#039;s notes after the moola&#039;s with nothing saying which&lt;br /&gt;
    // was which - the cards were indistinguishable. Each note is filed&lt;br /&gt;
    // under the heading of the document it came from: the page&#039;s own notes&lt;br /&gt;
    // first, then one section per commentary, in the order the page shows&lt;br /&gt;
    // them. common.js wraps injected notes in .gr-teeka-notes-group with a&lt;br /&gt;
    // .gr-teeka-notes-title, which is where the heading comes from.&lt;br /&gt;
    function groupOf(li) {&lt;br /&gt;
      var wrap = li.closest ? li.closest(&#039;.gr-teeka-notes-group&#039;) : null;&lt;br /&gt;
      if (!wrap) return null;&lt;br /&gt;
      var t = wrap.querySelector(&#039;.gr-teeka-notes-title&#039;);&lt;br /&gt;
      return {&lt;br /&gt;
        slug: wrap.getAttribute(&#039;data-teeka-slug&#039;) || &#039;&#039;,&lt;br /&gt;
        title: t ? t.textContent.trim() : &#039;&#039;&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var sections = [];          // [{key, title, items: []}] in document order&lt;br /&gt;
    var byKey = {};&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var g = groupOf(li);&lt;br /&gt;
      var key = g ? g.slug : &#039;&#039;;&lt;br /&gt;
      if (!byKey[key]) {&lt;br /&gt;
        byKey[key] = { key: key, title: g ? g.title : &#039;&#039;, items: [] };&lt;br /&gt;
        sections.push(byKey[key]);&lt;br /&gt;
      }&lt;br /&gt;
      byKey[key].items.push(li);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    sections.forEach(function (sec) {&lt;br /&gt;
      if (sec.title) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title&amp;quot;&amp;gt;&#039; + esc(sec.title) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      } else if (sections.length &amp;gt; 1) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title gra-footnote-section-own&amp;quot;&amp;gt;&#039;&lt;br /&gt;
              + esc(pageOwnNotesLabel()) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      }&lt;br /&gt;
      sec.items.forEach(function (li) { html += footnoteCard(li, sec.title); });&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The heading for the page&#039;s own notes, shown only when there is at&lt;br /&gt;
  // least one commentary section to tell them apart from.&lt;br /&gt;
  function pageOwnNotesLabel() {&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return (t &amp;amp;&amp;amp; t.textContent.trim()) || &#039;This page&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function footnoteCard(li, groupTitle) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      // The commentary&#039;s name is repeated on the card itself, not only on&lt;br /&gt;
      // the section heading: a reader who has scrolled the panel, or who&lt;br /&gt;
      // arrives at a card by clicking a word in the text, sees the card&lt;br /&gt;
      // without its heading in view.&lt;br /&gt;
      return &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (groupTitle ? &#039;&amp;lt;div class=&amp;quot;gra-footnote-source&amp;quot;&amp;gt;&#039;+esc(groupTitle)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-cite-copy&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-cite-copy&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $btn = $(this), id = $btn.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      var cite = buildCitation(rec);&lt;br /&gt;
      var what = $btn.attr(&#039;data-cite-what&#039;) || &#039;reference&#039;;&lt;br /&gt;
      // Only the reference carries formatting worth preserving; the in-text&lt;br /&gt;
      // form and the bare link must stay plain so they survive a paste into&lt;br /&gt;
      // the address bar.&lt;br /&gt;
      var text = cite[what] || cite.reference;&lt;br /&gt;
      var html = (what === &#039;reference&#039;) ? cite.referenceHtml : null;&lt;br /&gt;
      var was = $btn.text();&lt;br /&gt;
      copyToClipboard(text, function(){&lt;br /&gt;
        $btn.text(&#039;Copied&#039;).addClass(&#039;gra-cite-copy--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $btn.text(was).removeClass(&#039;gra-cite-copy--done&#039;); }, 1600);&lt;br /&gt;
      }, html);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
    hydrateCitationSources();&lt;br /&gt;
    // Commentary blocks arrive after the page does, so hydrate again when&lt;br /&gt;
    // they land rather than only once at boot.&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
      hydrateCitationSources(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  function updateBadge() {&lt;br /&gt;
    var n = _notes.length + _bookmarks.length;&lt;br /&gt;
    var $b = $(&#039;#gra-toggle-badge&#039;);&lt;br /&gt;
    if (!$b.length) return;&lt;br /&gt;
    $b.text(n &amp;gt; 99 ? &#039;99+&#039; : String(n)).css(&#039;display&#039;, n ? &#039;block&#039; : &#039;none&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7585</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7585"/>
		<updated>2026-09-30T07:10:30Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaGranthaId: td.doc.granthaId || &#039;&#039;,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-grantha-id&#039;, entry.teekaGranthaId || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                if (window.grLibrary) {&lt;br /&gt;
                    (function (el, gid, page) {&lt;br /&gt;
                        // The id may come from the teeka&#039;s own JSON (if that&lt;br /&gt;
                        // document has been rebuilt) or from the map page.&lt;br /&gt;
                        // Either way the commentary cites ITS work, not the&lt;br /&gt;
                        // moola it is displayed inside.&lt;br /&gt;
                        var got = gid ? Promise.resolve(gid)&lt;br /&gt;
                                      : window.grLibrary.idForPage(page, null);&lt;br /&gt;
                        got.then(function (id) {&lt;br /&gt;
                            if (!id) return null;&lt;br /&gt;
                            el.setAttribute(&#039;data-cite-grantha-id&#039;, id);&lt;br /&gt;
                            return window.grLibrary.lookup(id);&lt;br /&gt;
                        }).then(function (g) {&lt;br /&gt;
                            if (!g) return;&lt;br /&gt;
                            if (g.name)       el.setAttribute(&#039;data-cite-title&#039;, g.name);&lt;br /&gt;
                            if (g.nameIAST)   el.setAttribute(&#039;data-cite-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                            if (g.author)     el.setAttribute(&#039;data-cite-author&#039;, g.author);&lt;br /&gt;
                            if (g.authorIAST) el.setAttribute(&#039;data-cite-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                            var h = el.querySelector(&#039;.teeka-title&#039;);&lt;br /&gt;
                            if (h &amp;amp;&amp;amp; g.name) h.textContent = g.name;&lt;br /&gt;
                        });&lt;br /&gt;
                    }($entry[0], entry.teekaGranthaId, entry.teekaPage));&lt;br /&gt;
                }&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── HASH REVEAL ───────────────────────────────────────────────────────&lt;br /&gt;
   A copied id or a bookmark citation is only useful if its #link actually&lt;br /&gt;
   lands on the block. The browser&#039;s own fragment scroll fires once, at&lt;br /&gt;
   parse time, and then our fonts swap in and the teeka blocks inject -&lt;br /&gt;
   the page grows under the reader and the target drifts off screen. It&lt;br /&gt;
   looks exactly like the link didn&#039;t work.&lt;br /&gt;
&lt;br /&gt;
   This retries until the element exists and re-settles after late content&lt;br /&gt;
   arrives. It used to live in copyId.js (Editor extension), which is no&lt;br /&gt;
   longer loaded on this wiki, so it moves here where it always runs. If&lt;br /&gt;
   copyId IS loaded it does the same job; both are idempotent, and the&lt;br /&gt;
   guard below keeps them from fighting over the same jump. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grHashReveal) return;&lt;br /&gt;
&lt;br /&gt;
    var FLASH_MS = 2600;&lt;br /&gt;
    var _last = null;&lt;br /&gt;
&lt;br /&gt;
    function findTarget(id) {&lt;br /&gt;
        if (!id) return null;&lt;br /&gt;
        var esc = (window.CSS &amp;amp;&amp;amp; CSS.escape) ? CSS.escape(id) : id.replace(/[&amp;quot;\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
        return document.getElementById(id) ||&lt;br /&gt;
               document.querySelector(&#039;[data-block-id=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               document.querySelector(&#039;[data-copyid=&amp;quot;&#039; + esc + &#039;&amp;quot;]&#039;) ||&lt;br /&gt;
               null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function currentHash() {&lt;br /&gt;
        var raw = (location.hash || &#039;&#039;).slice(1);&lt;br /&gt;
        if (!raw) return &#039;&#039;;&lt;br /&gt;
        try { return decodeURIComponent(raw); } catch (e) { return raw; }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function land(el, flash) {&lt;br /&gt;
        el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;smooth&#039; });&lt;br /&gt;
        if (!flash) return;&lt;br /&gt;
        el.classList.add(&#039;gid-target&#039;);&lt;br /&gt;
        setTimeout(function () { el.classList.remove(&#039;gid-target&#039;); }, FLASH_MS);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function reveal(attemptsLeft, flash) {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            if (attemptsLeft &amp;gt; 0) setTimeout(function () { reveal(attemptsLeft - 1, flash); }, 400);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        _last = id;&lt;br /&gt;
        land(el, flash);&lt;br /&gt;
        // Re-settle once the late arrivals (fonts, injected teekas) have&lt;br /&gt;
        // shifted the layout. Cheap, and it is what makes the link feel&lt;br /&gt;
        // like it worked rather than like it missed.&lt;br /&gt;
        setTimeout(function () {&lt;br /&gt;
            if (currentHash() !== id) return;&lt;br /&gt;
            var again = findTarget(id);&lt;br /&gt;
            if (again) again.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
        }, 1200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function onLateContent() {&lt;br /&gt;
        var id = currentHash();&lt;br /&gt;
        if (!id || id !== _last) return;&lt;br /&gt;
        var el = findTarget(id);&lt;br /&gt;
        if (el) el.scrollIntoView({ block: &#039;center&#039;, behavior: &#039;auto&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grHashReveal = function (attempts) { reveal(attempts || 12, true); };&lt;br /&gt;
&lt;br /&gt;
    function boot() {&lt;br /&gt;
        reveal(12, true);&lt;br /&gt;
        window.addEventListener(&#039;hashchange&#039;, function () { reveal(6, true); });&lt;br /&gt;
        window.addEventListener(&#039;gr-new-content&#039;, onLateContent);&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(onLateContent);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── GRANTHA LIBRARY ───────────────────────────────────────────────────&lt;br /&gt;
   The colophon in the source docx says &amp;quot;श्रीमद्वादिराजपूज्यचरणविरचिता&lt;br /&gt;
   प्रकाशिका&amp;quot; - an author phrase welded to a generic label. That is not a&lt;br /&gt;
   citable title, and it is what the client saw as &amp;quot;Vadirajathirtha&lt;br /&gt;
   virachitha&amp;quot;. The Grantha library holds the real title and author, so&lt;br /&gt;
   the page carries only the library&#039;s stable id (data-grantha-id) and we&lt;br /&gt;
   resolve the rest here, at read time. Correct a name in the library and&lt;br /&gt;
   every citation on the site is correct on the next load - nothing to&lt;br /&gt;
   rebuild, nothing to re-upload.&lt;br /&gt;
&lt;br /&gt;
   Reads only, and the library&#039;s GET endpoints are public: no API key is&lt;br /&gt;
   sent from the browser, and none may ever be put in this file, which&lt;br /&gt;
   any reader can fetch. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (window.grLibrary) return;&lt;br /&gt;
&lt;br /&gt;
    // Same-origin by default so no CORS is needed. Override before this&lt;br /&gt;
    // file loads if the library app lives on another host.&lt;br /&gt;
    var API = (window.GR_LIBRARY_API || &#039;/api/granthas&#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    var TTL = 6 * 60 * 60 * 1000;                 // names change rarely&lt;br /&gt;
    var mem = {};&lt;br /&gt;
&lt;br /&gt;
    function cacheGet(id) {&lt;br /&gt;
        if (mem[id]) return mem[id];&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:&#039; + id);&lt;br /&gt;
            if (!raw) return null;&lt;br /&gt;
            var o = JSON.parse(raw);&lt;br /&gt;
            if (!o || (Date.now() - o.t) &amp;gt; TTL) return null;&lt;br /&gt;
            mem[id] = o.v;&lt;br /&gt;
            return o.v;&lt;br /&gt;
        } catch (e) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function cacheSet(id, v) {&lt;br /&gt;
        mem[id] = v;&lt;br /&gt;
        try { sessionStorage.setItem(&#039;gr-lib:&#039; + id, JSON.stringify({ t: Date.now(), v: v })); }&lt;br /&gt;
        catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The library returns camelCase fields; normalise to what the citation&lt;br /&gt;
    // builder wants and keep BOTH scripts, since a Sanskrit reference is&lt;br /&gt;
    // cited in Devanagari and an English-language one in IAST.&lt;br /&gt;
    function shape(g) {&lt;br /&gt;
        if (!g) return null;&lt;br /&gt;
        return {&lt;br /&gt;
            id:          g.id || &#039;&#039;,&lt;br /&gt;
            name:        g.name || &#039;&#039;,&lt;br /&gt;
            nameIAST:    g.transliteration || &#039;&#039;,&lt;br /&gt;
            author:      g.author || &#039;&#039;,&lt;br /&gt;
            authorIAST:  g.authorTransliteration || g.author_transliteration || &#039;&#039;,&lt;br /&gt;
            category:    g.category || &#039;&#039;,&lt;br /&gt;
            prasthana:   g.prasthana || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var inflight = {};&lt;br /&gt;
    function lookup(id) {&lt;br /&gt;
        if (!id) return Promise.resolve(null);&lt;br /&gt;
        var hit = cacheGet(id);&lt;br /&gt;
        if (hit) return Promise.resolve(hit);&lt;br /&gt;
        if (inflight[id]) return inflight[id];&lt;br /&gt;
        inflight[id] = fetch(API + &#039;/&#039; + encodeURIComponent(id), {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;library &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        }).then(function (j) {&lt;br /&gt;
            var v = shape(j &amp;amp;&amp;amp; (j.grantha || j.data || j));&lt;br /&gt;
            if (v &amp;amp;&amp;amp; v.name) cacheSet(id, v);&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return v;&lt;br /&gt;
        }).catch(function () {&lt;br /&gt;
            // Offline, blocked, or the id is not in the library yet. The&lt;br /&gt;
            // page&#039;s own data-author/data-doc-title stand in; a citation&lt;br /&gt;
            // is never blocked on this request.&lt;br /&gt;
            delete inflight[id];&lt;br /&gt;
            return null;&lt;br /&gt;
        });&lt;br /&gt;
        return inflight[id];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── WHICH GRANTHA IS THIS PAGE? ────────────────────────────────────&lt;br /&gt;
    // A page can say so itself (data-grantha-id, written by the pipeline),&lt;br /&gt;
    // but that would mean re-importing every document just to add one&lt;br /&gt;
    // attribute - and a re-import risks losing edits made on the site&lt;br /&gt;
    // since. So the mapping also lives in ONE wiki page, keyed by page&lt;br /&gt;
    // name, and the page looks itself up. Adding a document to the&lt;br /&gt;
    // library is then a single edit to that one page, with no content&lt;br /&gt;
    // touched anywhere.&lt;br /&gt;
    var MAP_PAGE = window.GR_GRANTHA_MAP_PAGE || &#039;MediaWiki:GranthaMap.json&#039;;&lt;br /&gt;
    var mapPromise = null;&lt;br /&gt;
&lt;br /&gt;
    function granthaMap() {&lt;br /&gt;
        if (mapPromise) return mapPromise;&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = sessionStorage.getItem(&#039;gr-lib:map&#039;);&lt;br /&gt;
            var o = raw &amp;amp;&amp;amp; JSON.parse(raw);&lt;br /&gt;
            if (o &amp;amp;&amp;amp; (Date.now() - o.t) &amp;lt; TTL) cached = o.v;&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        if (cached) { mapPromise = Promise.resolve(cached); return mapPromise; }&lt;br /&gt;
&lt;br /&gt;
        var url = (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl)&lt;br /&gt;
            ? mw.util.getUrl(MAP_PAGE, { action: &#039;raw&#039;, ctype: &#039;application/json&#039; })&lt;br /&gt;
            : &#039;/index.php?title=&#039; + encodeURIComponent(MAP_PAGE) + &#039;&amp;amp;action=raw&#039;;&lt;br /&gt;
        mapPromise = fetch(url, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.json() : {}; })&lt;br /&gt;
            .then(function (m) {&lt;br /&gt;
                // Either a flat {page: id} object, or {pages:{...},&lt;br /&gt;
                // locators:{...}} when division names are recorded too.&lt;br /&gt;
                m = m || {};&lt;br /&gt;
                if (!m.pages) m = { pages: m, locators: {} };&lt;br /&gt;
                m.locators = m.locators || {};&lt;br /&gt;
                try { sessionStorage.setItem(&#039;gr-lib:map&#039;, JSON.stringify({ t: Date.now(), v: m })); }&lt;br /&gt;
                catch (e) { }&lt;br /&gt;
                return m;&lt;br /&gt;
            })&lt;br /&gt;
            .catch(function () { return {}; });      // no map page yet - fine&lt;br /&gt;
        return mapPromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // page name -&amp;gt; library id, however it is known. The attribute wins,&lt;br /&gt;
    // so a rebuilt page needs no map entry and a map entry needs no rebuild.&lt;br /&gt;
    function idForPage(pageName, el) {&lt;br /&gt;
        var own = el &amp;amp;&amp;amp; el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-grantha-id&#039;);&lt;br /&gt;
        if (own) return Promise.resolve(own);&lt;br /&gt;
        if (!pageName) return Promise.resolve(&#039;&#039;);&lt;br /&gt;
        return granthaMap().then(function (m) {&lt;br /&gt;
            var p = m.pages || {};&lt;br /&gt;
            return p[pageName] || p[String(pageName).replace(/_/g, &#039; &#039;)] || &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Resolve this page and hand the result to anything listening (the&lt;br /&gt;
    // citation builder). Also fills in the visible title when the library&lt;br /&gt;
    // and the page disagree, so the reader sees the real name.&lt;br /&gt;
    function resolvePage() {&lt;br /&gt;
        var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        if (!t) return;&lt;br /&gt;
        var page = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        // Division names (adhyaya / valli / mantra) come from the same&lt;br /&gt;
        // map page, so a locator can be spelled out without a rebuild too.&lt;br /&gt;
        if (!t.getAttribute(&#039;data-locator-scheme&#039;)) {&lt;br /&gt;
            granthaMap().then(function (m) {&lt;br /&gt;
                var sc = (m.locators || {})[page] ||&lt;br /&gt;
                         (m.locators || {})[String(page).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (sc) t.setAttribute(&#039;data-locator-scheme&#039;, sc);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        idForPage(page, t).then(function (gid) {&lt;br /&gt;
            if (!gid) return;&lt;br /&gt;
            t.setAttribute(&#039;data-grantha-id&#039;, gid);&lt;br /&gt;
            return lookup(gid).then(function (g) {&lt;br /&gt;
                if (!g) return;&lt;br /&gt;
                if (g.name)       t.setAttribute(&#039;data-doc-title&#039;, g.name);&lt;br /&gt;
                if (g.nameIAST)   t.setAttribute(&#039;data-doc-title-iast&#039;, g.nameIAST);&lt;br /&gt;
                if (g.author)     t.setAttribute(&#039;data-author&#039;, g.author);&lt;br /&gt;
                if (g.authorIAST) t.setAttribute(&#039;data-author-iast&#039;, g.authorIAST);&lt;br /&gt;
                try {&lt;br /&gt;
                    window.dispatchEvent(new CustomEvent(&#039;gr-library-resolved&#039;, { detail: g }));&lt;br /&gt;
                } catch (e) { }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetching the whole catalogue is one request; fetching fifty records&lt;br /&gt;
    // one at a time is fifty. Past a handful, take the catalogue.&lt;br /&gt;
    var cataloguePromise = null;&lt;br /&gt;
    function catalogue() {&lt;br /&gt;
        if (cataloguePromise) return cataloguePromise;&lt;br /&gt;
        cataloguePromise = fetch(API + &#039;?limit=1000&#039;, {&lt;br /&gt;
            credentials: &#039;omit&#039;, headers: { &#039;Accept&#039;: &#039;application/json&#039; }&lt;br /&gt;
        }).then(function (r) { return r.ok ? r.json() : null; })&lt;br /&gt;
          .then(function (j) {&lt;br /&gt;
              var list = (j &amp;amp;&amp;amp; (j.granthas || j.data || j.results)) || [];&lt;br /&gt;
              var by = {};&lt;br /&gt;
              list.forEach(function (g) { var v = shape(g); if (v &amp;amp;&amp;amp; v.id) { by[v.id] = v; cacheSet(v.id, v); } });&lt;br /&gt;
              return by;&lt;br /&gt;
          }).catch(function () { return {}; });&lt;br /&gt;
        return cataloguePromise;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function lookupMany(ids) {&lt;br /&gt;
        var want = [], seen = {};&lt;br /&gt;
        (ids || []).forEach(function (i) { if (i &amp;amp;&amp;amp; !seen[i]) { seen[i] = 1; want.push(i); } });&lt;br /&gt;
        var missing = want.filter(function (i) { return !cacheGet(i); });&lt;br /&gt;
        if (missing.length &amp;gt; 5) {&lt;br /&gt;
            return catalogue().then(function (by) {&lt;br /&gt;
                var out = {};&lt;br /&gt;
                want.forEach(function (i) { out[i] = by[i] || cacheGet(i) || null; });&lt;br /&gt;
                return out;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all(want.map(lookup)).then(function (vals) {&lt;br /&gt;
            var out = {};&lt;br /&gt;
            want.forEach(function (i, n) { out[i] = vals[n]; });&lt;br /&gt;
            return out;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── DOCUMENT LINKS ────────────────────────────────────────────────&lt;br /&gt;
    // The home page&#039;s cards were written by hand and carry the same&lt;br /&gt;
    // colophon labels the citation had to stop using - &amp;quot;प्रकाशिका&amp;quot;,&lt;br /&gt;
    // &amp;quot;भाष्यम्&amp;quot; - which say what KIND of work it is, not which one. Any&lt;br /&gt;
    // link to a mapped page gets the library&#039;s name instead, so the&lt;br /&gt;
    // catalogue and the site agree without the page being edited.&lt;br /&gt;
    function relabelDocLinks(root) {&lt;br /&gt;
        var scope = root || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;.gr-home-grid a, .gr-doc-nav a, .gr-vadavali a&#039;);&lt;br /&gt;
        if (!links.length) return;&lt;br /&gt;
        granthaMap().then(function (m) {&lt;br /&gt;
            var pages = m.pages || {};&lt;br /&gt;
            var jobs = [];&lt;br /&gt;
            Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
                if (a.getAttribute(&#039;data-gr-relabelled&#039;)) return;&lt;br /&gt;
                var t = a.getAttribute(&#039;title&#039;) ||&lt;br /&gt;
                        decodeURIComponent((a.getAttribute(&#039;href&#039;) || &#039;&#039;).split(&#039;#&#039;)[0]&lt;br /&gt;
                            .replace(/^.*\/(?:wiki\/)?/, &#039;&#039;));&lt;br /&gt;
                var gid = pages[t] || pages[String(t).replace(/_/g, &#039; &#039;)];&lt;br /&gt;
                if (gid) jobs.push([a, gid]);&lt;br /&gt;
            });&lt;br /&gt;
            if (!jobs.length) return;&lt;br /&gt;
            lookupMany(jobs.map(function (j) { return j[1]; })).then(function (by) {&lt;br /&gt;
                jobs.forEach(function (j) {&lt;br /&gt;
                    var g = by[j[1]];&lt;br /&gt;
                    if (!g || !g.name) return;&lt;br /&gt;
                    j[0].setAttribute(&#039;data-gr-relabelled&#039;, &#039;1&#039;);&lt;br /&gt;
                    j[0].setAttribute(&#039;data-grantha-id&#039;, j[1]);&lt;br /&gt;
                    if (g.authorIAST || g.author) {&lt;br /&gt;
                        j[0].setAttribute(&#039;title&#039;, (g.author || &#039;&#039;) + &#039; — &#039; + g.name);&lt;br /&gt;
                    }&lt;br /&gt;
                    j[0].textContent = g.name;&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue,&lt;br /&gt;
                         idForPage: idForPage, map: granthaMap, api: API,&lt;br /&gt;
                         relabelDocLinks: relabelDocLinks };&lt;br /&gt;
    function boot() { resolvePage(); relabelDocLinks(); }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        relabelDocLinks(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:GranthaMap.json&amp;diff=7584</id>
		<title>MediaWiki:GranthaMap.json</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:GranthaMap.json&amp;diff=7584"/>
		<updated>2026-09-30T07:09:43Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: Created page with &amp;quot;{  &amp;quot;pages&amp;quot;: {   &amp;quot;Chandogya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,   &amp;quot;Chandogya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,   &amp;quot;Chandogya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.09.02&amp;quot;,   &amp;quot;Chandogya/Vyakhya/VedeshaPadarthakaumudi&amp;quot;: &amp;quot;UP.09.01.02&amp;quot;,   &amp;quot;Eeshavasya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,   &amp;quot;Eeshavasya/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.01.01.02&amp;quot;,   &amp;quot;Eeshavasya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,   &amp;quot;Eeshavasya/Vyakhya/NrsimhacharyaDurgamavakyarthavivarana&amp;quot;: &amp;quot;UP.01.01.02.04&amp;quot;,   &amp;quot;Eeshavasya/Vyakhya/RaghavendraKhandartha...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
	&amp;quot;pages&amp;quot;: {&lt;br /&gt;
		&amp;quot;Chandogya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.09.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Chandogya/Vyakhya/VedeshaPadarthakaumudi&amp;quot;: &amp;quot;UP.09.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.01.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/NrsimhacharyaDurgamavakyarthavivarana&amp;quot;: &amp;quot;UP.01.01.02.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/RaghunathaVivriti&amp;quot;: &amp;quot;UP.01.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/SrinivasaVyakhyana&amp;quot;: &amp;quot;UP.01.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Eeshavasya/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.01.01.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/JayatirthaPrameyadipika&amp;quot;: &amp;quot;GP.01.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;GP.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Gitabhashya_5_8/Vyakhya/SumatindraBhavaratnakosha&amp;quot;: &amp;quot;GP.01.01.03.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha&amp;quot;: &amp;quot;UP.03.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.03.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.03.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VaradatirthaCommentary&amp;quot;: &amp;quot;UP.03.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VedeshatirthaVivarana&amp;quot;: &amp;quot;UP.03.01.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot;: &amp;quot;UP.03.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Mahaitareya&amp;quot;: &amp;quot;UP.08.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/JayatirthaTika&amp;quot;: &amp;quot;UP.04.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.04.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.04.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/SrinivasaTippani&amp;quot;: &amp;quot;UP.04.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Shatprashna/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.04.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara&amp;quot;: &amp;quot;UP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/MadhvacharyaBhashya&amp;quot;: &amp;quot;UP.02.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/RaghavendraKhandartha&amp;quot;: &amp;quot;UP.02.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VadirajaPrakashika&amp;quot;: &amp;quot;UP.02.01.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VaradaTika&amp;quot;: &amp;quot;UP.02.01.03&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VedeshaVivarana&amp;quot;: &amp;quot;UP.02.01.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Talavakara/Vyakhya/VyasatirthaVivarana&amp;quot;: &amp;quot;UP.02.01.02&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/GudhakartrikaTippani&amp;quot;: &amp;quot;SA.05.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/MangaliTantrasarasangrahatippani&amp;quot;: &amp;quot;SA.05.10&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/RaghunathaTattvakanika&amp;quot;: &amp;quot;SA.05.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VasudhendraTantrasarasangrahavivarana&amp;quot;: &amp;quot;SA.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VedavedyaTantrasaravivriti&amp;quot;: &amp;quot;SA.05.05&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VedavyasaPadabhavaprabodhini&amp;quot;: &amp;quot;SA.05.12&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VenkatanarasimhacharyaTantrasaravivriti&amp;quot;: &amp;quot;SA.05.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Tantrasara_Sangraha/Vyakhya/VenkatapatyacharyaGurvarthadidhiti&amp;quot;: &amp;quot;SA.05.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti&amp;quot;: &amp;quot;RC.09&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/BhagavantarayaLaghunigudharthaprakashika&amp;quot;: &amp;quot;RC.09.07&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/KurmaBhavapradipika&amp;quot;: &amp;quot;RC.09.06&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/SrinivasaShrinivasatirthiya&amp;quot;: &amp;quot;RC.09.01&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/VedatmaKavikarnamrita&amp;quot;: &amp;quot;RC.09.04&amp;quot;,&lt;br /&gt;
		&amp;quot;Vayustuti/Vyakhya/VishwapatiBalabodhini&amp;quot;: &amp;quot;RC.09.02&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;locators&amp;quot;: {&lt;br /&gt;
		&amp;quot;Katha&amp;quot;: &amp;quot;C=Adhyāya,S=Vallī,V=Mantra,B=Bhāṣya&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7580</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7580"/>
		<updated>2026-09-29T11:07:26Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaAuthor: td.doc.author || &#039;&#039;,&lt;br /&gt;
                    teekaPage: td.doc.pageTitle || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    // Citation metadata: a bookmark taken inside an&lt;br /&gt;
                    // interleaved commentary must cite THAT work and its&lt;br /&gt;
                    // author, and link to the commentary&#039;s own page - not&lt;br /&gt;
                    // to the moola whose page it happens to be shown on.&lt;br /&gt;
                    .attr(&#039;data-cite-author&#039;, entry.teekaAuthor || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-title&#039;, entry.teekaTitle || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;data-cite-page&#039;, entry.teekaPage || &#039;&#039;)&lt;br /&gt;
                    .attr(&#039;id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7579</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7579"/>
		<updated>2026-09-29T11:07:04Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  /* CHANGED from inline-flex + vertical-align:middle. An inline-flex box&lt;br /&gt;
     aligned &amp;quot;middle&amp;quot; is positioned by its own height against the parent&#039;s&lt;br /&gt;
     x-height, so the whole control - checkbox AND label together - sat&lt;br /&gt;
     progressively lower than the text beside it as the reader enlarged the&lt;br /&gt;
     type (measured: 1.8px out at 16px, 3.6px at 30px). Left as plain&lt;br /&gt;
     inline, the label is ordinary text and lines up with its neighbours by&lt;br /&gt;
     definition; only the checkbox needs aligning, and it does that with an&lt;br /&gt;
     em-based offset that holds at any size. */&lt;br /&gt;
  display: inline;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  /* Sized in em so it keeps pace with A- / A+ rather than staying a fixed&lt;br /&gt;
     15px box against text that grows; clamped so it stays a sensible&lt;br /&gt;
     target at both extremes.&lt;br /&gt;
     Alignment was measured rather than guessed. vertical-align:middle is&lt;br /&gt;
     the only value that stays consistent as the type size changes (it is&lt;br /&gt;
     computed from the font&#039;s x-height), but it sits about 2.5px low&lt;br /&gt;
     against this line-height at every size - so that constant bias is&lt;br /&gt;
     taken back with `top`, giving under a pixel of error from 16px to&lt;br /&gt;
     30px. The element is already position:relative for its tick. */&lt;br /&gt;
  width: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  height: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  min-width: 13px; min-height: 13px;&lt;br /&gt;
  margin: 0 6px 0 0; padding: 0; cursor: pointer;&lt;br /&gt;
  vertical-align: middle; top: -2.5px;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  /* Placed and sized in % of the box, so the tick follows the em-based&lt;br /&gt;
     box instead of staying a 4x8px mark inside a checkbox that grew. */&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 32%; top: 8%;&lt;br /&gt;
  width: 26%; height: 55%;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── BOOKMARK CITATION ──────────────────────────────────────────────&lt;br /&gt;
   The reference a bookmark produces, shown on its card and copyable in&lt;br /&gt;
   one click. Set small and in the UI face deliberately: it is apparatus&lt;br /&gt;
   for the reader to take away, not part of the text. */&lt;br /&gt;
.gra-bookmark-cite {&lt;br /&gt;
  margin-top: 6px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.54), 15px);&lt;br /&gt;
  line-height: 1.45; color: #6b5b52;&lt;br /&gt;
  overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gra-cite-copy {&lt;br /&gt;
  margin-top: 6px; padding: 3px 10px;&lt;br /&gt;
  font-family: var(--font-ui);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  background: #fff5f2; border: 1px solid #f0cfc4; border-radius: 20px;&lt;br /&gt;
  cursor: pointer; transition: background .15s, color .15s, border-color .15s;&lt;br /&gt;
}&lt;br /&gt;
.gra-cite-copy:hover { background: #b5451b; color: #fff; border-color: #b5451b; }&lt;br /&gt;
.gra-cite-copy--done { background: #2e6e40; color: #fff; border-color: #2e6e40; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7578</id>
		<title>MediaWiki:Gadget-GrAnnotations.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7578"/>
		<updated>2026-09-29T11:06:43Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span id=&amp;quot;gra-toggle-badge&amp;quot; aria-live=&amp;quot;polite&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* ── SHAREABLE CITATION ────────────────────────────────────────────&lt;br /&gt;
     A bookmark is personal, but what it produces is not: a reference in&lt;br /&gt;
     APA form plus a link that resolves for anyone.&lt;br /&gt;
&lt;br /&gt;
     The link cannot point at the bookmark itself. A bookmark wraps the&lt;br /&gt;
     reader&#039;s own selection in a span with an id generated in their&lt;br /&gt;
     browser; that span is never saved to the page, so #&amp;lt;bookmark-id&amp;gt;&lt;br /&gt;
     resolves for nobody else. The citation therefore anchors to the&lt;br /&gt;
     nearest enclosing block that the PUBLISHED page carries an id for -&lt;br /&gt;
     the same ids copyId.js links to, and the same ones cross-references&lt;br /&gt;
     across the site already use.&lt;br /&gt;
&lt;br /&gt;
     APA 7th, for a work on a website with no date:&lt;br /&gt;
       Author. (n.d.). Title (Locator). Site. Retrieved D Month Y, from URL&lt;br /&gt;
     n.d. because these works carry no publication date, and a retrieval&lt;br /&gt;
     date because a wiki page can change. The block reference is given as&lt;br /&gt;
     a parenthetical locator after the title, which is where APA puts a&lt;br /&gt;
     section or part designation. */&lt;br /&gt;
&lt;br /&gt;
  var CITE_BLOCK_SEL = &#039;.verse-block[id], .bhashyam-block[id], .teeka-block[id], &#039; +&lt;br /&gt;
                       &#039;[data-block-id][id], .gr-apparatus[id], .shloka[id]&#039;;&lt;br /&gt;
&lt;br /&gt;
  function citeAnchorFor(node) {&lt;br /&gt;
    var el = node &amp;amp;&amp;amp; node.nodeType === 3 ? node.parentNode : node;&lt;br /&gt;
    while (el &amp;amp;&amp;amp; el !== document.body) {&lt;br /&gt;
      if (el.matches &amp;amp;&amp;amp; el.matches(CITE_BLOCK_SEL)) return el;&lt;br /&gt;
      el = el.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Walks up from the cited block: an interleaved commentary block states&lt;br /&gt;
  // its own work, otherwise the page&#039;s title element does.&lt;br /&gt;
  function citeSourceFor(el) {&lt;br /&gt;
    var n = el;&lt;br /&gt;
    while (n &amp;amp;&amp;amp; n !== document.body) {&lt;br /&gt;
      if (n.getAttribute &amp;amp;&amp;amp; n.getAttribute(&#039;data-cite-title&#039;)) {&lt;br /&gt;
        return {&lt;br /&gt;
          author: n.getAttribute(&#039;data-cite-author&#039;) || &#039;&#039;,&lt;br /&gt;
          title:  n.getAttribute(&#039;data-cite-title&#039;) || &#039;&#039;,&lt;br /&gt;
          page:   n.getAttribute(&#039;data-cite-page&#039;) || &#039;&#039;&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      n = n.parentNode;&lt;br /&gt;
    }&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return {&lt;br /&gt;
      author: (t &amp;amp;&amp;amp; t.getAttribute(&#039;data-author&#039;)) || &#039;&#039;,&lt;br /&gt;
      title:  (t &amp;amp;&amp;amp; (t.getAttribute(&#039;data-doc-title&#039;) || t.textContent.trim())) || document.title,&lt;br /&gt;
      page:   (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;&lt;br /&gt;
    };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function citeUrl(pageName, anchorId) {&lt;br /&gt;
    var path = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
    var page = pageName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
    var base = path.replace(&#039;$1&#039;, String(page).split(&#039;/&#039;).map(encodeURIComponent).join(&#039;/&#039;));&lt;br /&gt;
    return location.origin + base + (anchorId ? &#039;#&#039; + encodeURIComponent(anchorId) : &#039;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var CITE_MONTHS = [&#039;January&#039;,&#039;February&#039;,&#039;March&#039;,&#039;April&#039;,&#039;May&#039;,&#039;June&#039;,&lt;br /&gt;
                     &#039;July&#039;,&#039;August&#039;,&#039;September&#039;,&#039;October&#039;,&#039;November&#039;,&#039;December&#039;];&lt;br /&gt;
&lt;br /&gt;
  function retrievedToday() {&lt;br /&gt;
    var d = new Date();&lt;br /&gt;
    return CITE_MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function siteName() {&lt;br /&gt;
    return (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgSiteName&#039;)) || location.hostname;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // rec: the bookmark record. Returns {text, url} - text is the full APA&lt;br /&gt;
  // reference with the link in it, ready to paste anywhere.&lt;br /&gt;
  function buildCitation(rec) {&lt;br /&gt;
    var src = rec.citeSource || {};&lt;br /&gt;
    var author = src.author || &#039;&#039;;&lt;br /&gt;
    var title  = src.title || &#039;&#039;;&lt;br /&gt;
    var anchor = rec.anchor || &#039;&#039;;&lt;br /&gt;
    var url    = citeUrl(src.page, anchor);&lt;br /&gt;
    var parts  = [];&lt;br /&gt;
    if (author) parts.push(author + &#039;.&#039;);&lt;br /&gt;
    parts.push(&#039;(n.d.).&#039;);&lt;br /&gt;
    parts.push(title + (anchor ? &#039; (&#039; + anchor + &#039;)&#039; : &#039;&#039;) + &#039;.&#039;);&lt;br /&gt;
    parts.push(siteName() + &#039;.&#039;);&lt;br /&gt;
    parts.push(&#039;Retrieved &#039; + retrievedToday() + &#039;, from &#039; + url);&lt;br /&gt;
    return { text: parts.join(&#039; &#039;), url: url };&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function copyToClipboard(text, done) {&lt;br /&gt;
    if (navigator.clipboard &amp;amp;&amp;amp; navigator.clipboard.writeText) {&lt;br /&gt;
      navigator.clipboard.writeText(text).then(done).catch(function () { legacyCopy(text, done); });&lt;br /&gt;
    } else { legacyCopy(text, done); }&lt;br /&gt;
  }&lt;br /&gt;
  function legacyCopy(text, done) {&lt;br /&gt;
    var ta = document.createElement(&#039;textarea&#039;);&lt;br /&gt;
    ta.value = text;&lt;br /&gt;
    ta.style.cssText = &#039;position:fixed;top:-9999px;left:-9999px;&#039;;&lt;br /&gt;
    document.body.appendChild(ta); ta.focus(); ta.select();&lt;br /&gt;
    try { document.execCommand(&#039;copy&#039;); done &amp;amp;&amp;amp; done(); } catch (e) {}&lt;br /&gt;
    document.body.removeChild(ta);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    // Resolve the citation anchor BEFORE wrapping: wrapSelection inserts a&lt;br /&gt;
    // span of its own, and resolving afterwards would just find that.&lt;br /&gt;
    var anchorEl = citeAnchorFor(_selRange &amp;amp;&amp;amp; _selRange.startContainer);&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    if (!anchorEl &amp;amp;&amp;amp; span) anchorEl = citeAnchorFor(span);&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso(),&lt;br /&gt;
               anchor: anchorEl ? (anchorEl.id || anchorEl.getAttribute(&#039;data-block-id&#039;) || &#039;&#039;) : &#039;&#039;,&lt;br /&gt;
               citeSource: citeSourceFor(anchorEl || document.body)};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      var cite = buildCitation(b);&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-cite&amp;quot;&amp;gt;&#039;+esc(cite.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gra-cite-copy&amp;quot; data-cite-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;Copy citation&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    // CHANGED: grouped by source. A moola page now also carries the notes&lt;br /&gt;
    // of every commentary shown on it, and flattening them into one list&lt;br /&gt;
    // put a commentary&#039;s notes after the moola&#039;s with nothing saying which&lt;br /&gt;
    // was which - the cards were indistinguishable. Each note is filed&lt;br /&gt;
    // under the heading of the document it came from: the page&#039;s own notes&lt;br /&gt;
    // first, then one section per commentary, in the order the page shows&lt;br /&gt;
    // them. common.js wraps injected notes in .gr-teeka-notes-group with a&lt;br /&gt;
    // .gr-teeka-notes-title, which is where the heading comes from.&lt;br /&gt;
    function groupOf(li) {&lt;br /&gt;
      var wrap = li.closest ? li.closest(&#039;.gr-teeka-notes-group&#039;) : null;&lt;br /&gt;
      if (!wrap) return null;&lt;br /&gt;
      var t = wrap.querySelector(&#039;.gr-teeka-notes-title&#039;);&lt;br /&gt;
      return {&lt;br /&gt;
        slug: wrap.getAttribute(&#039;data-teeka-slug&#039;) || &#039;&#039;,&lt;br /&gt;
        title: t ? t.textContent.trim() : &#039;&#039;&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var sections = [];          // [{key, title, items: []}] in document order&lt;br /&gt;
    var byKey = {};&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var g = groupOf(li);&lt;br /&gt;
      var key = g ? g.slug : &#039;&#039;;&lt;br /&gt;
      if (!byKey[key]) {&lt;br /&gt;
        byKey[key] = { key: key, title: g ? g.title : &#039;&#039;, items: [] };&lt;br /&gt;
        sections.push(byKey[key]);&lt;br /&gt;
      }&lt;br /&gt;
      byKey[key].items.push(li);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    sections.forEach(function (sec) {&lt;br /&gt;
      if (sec.title) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title&amp;quot;&amp;gt;&#039; + esc(sec.title) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      } else if (sections.length &amp;gt; 1) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title gra-footnote-section-own&amp;quot;&amp;gt;&#039;&lt;br /&gt;
              + esc(pageOwnNotesLabel()) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      }&lt;br /&gt;
      sec.items.forEach(function (li) { html += footnoteCard(li, sec.title); });&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The heading for the page&#039;s own notes, shown only when there is at&lt;br /&gt;
  // least one commentary section to tell them apart from.&lt;br /&gt;
  function pageOwnNotesLabel() {&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return (t &amp;amp;&amp;amp; t.textContent.trim()) || &#039;This page&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function footnoteCard(li, groupTitle) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      // The commentary&#039;s name is repeated on the card itself, not only on&lt;br /&gt;
      // the section heading: a reader who has scrolled the panel, or who&lt;br /&gt;
      // arrives at a card by clicking a word in the text, sees the card&lt;br /&gt;
      // without its heading in view.&lt;br /&gt;
      return &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (groupTitle ? &#039;&amp;lt;div class=&amp;quot;gra-footnote-source&amp;quot;&amp;gt;&#039;+esc(groupTitle)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-cite-copy&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-cite-copy&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var $btn = $(this), id = $btn.attr(&#039;data-cite-id&#039;);&lt;br /&gt;
      var rec = _bookmarks.filter(function(b){ return b.id === id; })[0];&lt;br /&gt;
      if (!rec) return;&lt;br /&gt;
      copyToClipboard(buildCitation(rec).text, function(){&lt;br /&gt;
        var was = $btn.text();&lt;br /&gt;
        $btn.text(&#039;Copied&#039;).addClass(&#039;gra-cite-copy--done&#039;);&lt;br /&gt;
        setTimeout(function(){ $btn.text(was).removeClass(&#039;gra-cite-copy--done&#039;); }, 1600);&lt;br /&gt;
      });&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  function updateBadge() {&lt;br /&gt;
    var n = _notes.length + _bookmarks.length;&lt;br /&gt;
    var $b = $(&#039;#gra-toggle-badge&#039;);&lt;br /&gt;
    if (!$b.length) return;&lt;br /&gt;
    $b.text(n &amp;gt; 99 ? &#039;99+&#039; : String(n)).css(&#039;display&#039;, n ? &#039;block&#039; : &#039;none&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7577</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7577"/>
		<updated>2026-09-29T10:37:01Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            // After the bhashya, not between the verse and it. The moola&lt;br /&gt;
            // page prints verse then bhashya as one unit, and inserting&lt;br /&gt;
            // straight after the verse element split them apart, putting&lt;br /&gt;
            // the commentaries ahead of the bhashya they comment on.&lt;br /&gt;
            // The bhashya-collection for a verse carries id&lt;br /&gt;
            // &amp;quot;bhashya-&amp;lt;verse oldKey&amp;gt;&amp;quot;; where a page has none, the verse&lt;br /&gt;
            // itself is still the anchor.&lt;br /&gt;
            var anchor = $verseEl;&lt;br /&gt;
            var coll = document.getElementById(&#039;bhashya-&#039; + verseId);&lt;br /&gt;
            if (coll &amp;amp;&amp;amp; coll.compareDocumentPosition($verseEl[0]) &amp;amp; Node.DOCUMENT_POSITION_PRECEDING) {&lt;br /&gt;
                anchor = $(coll);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Some pages emit the bhashya as a sibling block without&lt;br /&gt;
                // the collection wrapper - walk forward over any blocks&lt;br /&gt;
                // that belong to this same verse.&lt;br /&gt;
                var n = $verseEl[0].nextElementSibling;&lt;br /&gt;
                while (n &amp;amp;&amp;amp; n.getAttribute &amp;amp;&amp;amp;&lt;br /&gt;
                       (n.getAttribute(&#039;data-parent-verse&#039;) === verseId ||&lt;br /&gt;
                        n.id === &#039;bhashya-&#039; + verseId)) {&lt;br /&gt;
                    anchor = $(n);&lt;br /&gt;
                    n = n.nextElementSibling;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            anchor.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    //&lt;br /&gt;
                    // This MUST come before augmentExistingTeekaLinks:&lt;br /&gt;
                    // that builds each inline checkbox with&lt;br /&gt;
                    // .prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]), reading&lt;br /&gt;
                    // the selection as it stands at that moment. Selecting&lt;br /&gt;
                    // afterwards left every box drawn unchecked while the&lt;br /&gt;
                    // commentaries themselves were shown - the state and&lt;br /&gt;
                    // the control disagreeing on screen.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7576</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7576"/>
		<updated>2026-09-29T10:36:32Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  /* CHANGED from inline-flex + vertical-align:middle. An inline-flex box&lt;br /&gt;
     aligned &amp;quot;middle&amp;quot; is positioned by its own height against the parent&#039;s&lt;br /&gt;
     x-height, so the whole control - checkbox AND label together - sat&lt;br /&gt;
     progressively lower than the text beside it as the reader enlarged the&lt;br /&gt;
     type (measured: 1.8px out at 16px, 3.6px at 30px). Left as plain&lt;br /&gt;
     inline, the label is ordinary text and lines up with its neighbours by&lt;br /&gt;
     definition; only the checkbox needs aligning, and it does that with an&lt;br /&gt;
     em-based offset that holds at any size. */&lt;br /&gt;
  display: inline;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  /* Sized in em so it keeps pace with A- / A+ rather than staying a fixed&lt;br /&gt;
     15px box against text that grows; clamped so it stays a sensible&lt;br /&gt;
     target at both extremes.&lt;br /&gt;
     Alignment was measured rather than guessed. vertical-align:middle is&lt;br /&gt;
     the only value that stays consistent as the type size changes (it is&lt;br /&gt;
     computed from the font&#039;s x-height), but it sits about 2.5px low&lt;br /&gt;
     against this line-height at every size - so that constant bias is&lt;br /&gt;
     taken back with `top`, giving under a pixel of error from 16px to&lt;br /&gt;
     30px. The element is already position:relative for its tick. */&lt;br /&gt;
  width: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  height: clamp(13px, 0.8em, 24px) !important;&lt;br /&gt;
  min-width: 13px; min-height: 13px;&lt;br /&gt;
  margin: 0 6px 0 0; padding: 0; cursor: pointer;&lt;br /&gt;
  vertical-align: middle; top: -2.5px;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  /* Placed and sized in % of the box, so the tick follows the em-based&lt;br /&gt;
     box instead of staying a 4x8px mark inside a checkbox that grew. */&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 32%; top: 8%;&lt;br /&gt;
  width: 26%; height: 55%;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7575</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7575"/>
		<updated>2026-09-29T05:09:29Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
/* font-display: swap on every face below. Without it the browser uses&lt;br /&gt;
   `auto`, which in Chrome means a block period of up to three seconds&lt;br /&gt;
   during which text in that font is INVISIBLE - the page has painted, the&lt;br /&gt;
   layout is right, and the Sanskrit simply is not there yet. That reads as&lt;br /&gt;
   a slow site even when the bytes arrive quickly. With swap the text is&lt;br /&gt;
   painted immediately in the fallback and re-rendered when the face&lt;br /&gt;
   arrives. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7574</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7574"/>
		<updated>2026-09-29T05:08:34Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // One request for ALL of them, not one each. The API takes up to 50&lt;br /&gt;
    // titles at a time, and a moola page now shows every commentary by&lt;br /&gt;
    // default - so what used to be one round trip became five or ten,&lt;br /&gt;
    // fired together but each with its own latency, and the commentaries&lt;br /&gt;
    // appeared well after the text. Batching makes it a single request&lt;br /&gt;
    // whatever the page carries. maxage/smaxage let the browser and the&lt;br /&gt;
    // CDN reuse the response, so moving between chapters of one grantha&lt;br /&gt;
    // does not re-fetch the same commentaries.&lt;br /&gt;
    var teekaJsonCache = {};&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJsons(titles) {&lt;br /&gt;
        var want = titles.filter(function (t) { return !(t in teekaJsonCache); });&lt;br /&gt;
        if (!want.length) return $.Deferred().resolve(teekaJsonCache).promise();&lt;br /&gt;
&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; want.length; i += 40) batches.push(want.slice(i, i + 40));&lt;br /&gt;
&lt;br /&gt;
        return $.when.apply($, batches.map(function (batch) {&lt;br /&gt;
            return $.ajax({&lt;br /&gt;
                url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
                data: {&lt;br /&gt;
                    action: &#039;query&#039;,&lt;br /&gt;
                    titles: batch.map(function (t) { return &#039;JSON:&#039; + t; }).join(&#039;|&#039;),&lt;br /&gt;
                    prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;,&lt;br /&gt;
                    format: &#039;json&#039;, formatversion: 2,&lt;br /&gt;
                    maxage: 300, smaxage: 300&lt;br /&gt;
                },&lt;br /&gt;
                dataType: &#039;json&#039;&lt;br /&gt;
            }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
                pages.forEach(function (page) {&lt;br /&gt;
                    // Match the response back to the title we asked for:&lt;br /&gt;
                    // the API may normalise or reorder, so never pair by&lt;br /&gt;
                    // index. Strip the namespace the same way it was added.&lt;br /&gt;
                    var title = (page.title || &#039;&#039;).replace(/^JSON:/, &#039;&#039;);&lt;br /&gt;
                    if (page.missing) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots &amp;amp;&amp;amp; page.revisions[0].slots.main &amp;amp;&amp;amp;&lt;br /&gt;
                        page.revisions[0].slots.main.content;&lt;br /&gt;
                    if (!content) { teekaJsonCache[title] = null; return; }&lt;br /&gt;
                    try {&lt;br /&gt;
                        var parsed = JSON.parse(content);&lt;br /&gt;
                        parsed._title = title;&lt;br /&gt;
                        teekaJsonCache[title] = parsed;&lt;br /&gt;
                    } catch (e) { teekaJsonCache[title] = null; }&lt;br /&gt;
                });&lt;br /&gt;
            }).catch(function () {&lt;br /&gt;
                batch.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            });&lt;br /&gt;
        })).then(function () {&lt;br /&gt;
            // Anything the response never mentioned is recorded as absent,&lt;br /&gt;
            // so a later call does not ask for it again.&lt;br /&gt;
            want.forEach(function (t) { if (!(t in teekaJsonCache)) teekaJsonCache[t] = null; });&lt;br /&gt;
            return teekaJsonCache;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    // Not seeded from storage: every page load starts with all&lt;br /&gt;
    // commentaries on (see where availableList is filled). Unchecking one&lt;br /&gt;
    // still writes to storage, which keeps the control&#039;s own state&lt;br /&gt;
    // consistent within the page and leaves the value there for anything&lt;br /&gt;
    // else that reads it, but it is not what decides the initial view.&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            fetchTeekaJsons(subpages).then(function (cache) {&lt;br /&gt;
                var fetched = subpages.map(function (t) { return cache[t] || null; });&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    // Every commentary is on at the start of EVERY page&lt;br /&gt;
                    // load, not only a reader&#039;s first visit. The moola is&lt;br /&gt;
                    // published together with its commentaries and the&lt;br /&gt;
                    // complete text is what a reader should land on; the&lt;br /&gt;
                    // checkboxes are for narrowing it down while reading,&lt;br /&gt;
                    // and that narrowing lasts for as long as the page is&lt;br /&gt;
                    // open rather than following the reader to the next one.&lt;br /&gt;
                    availableList.forEach(function (it) {&lt;br /&gt;
                        selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                        (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                    });&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                fetchTeekaJsons(uniqueRelated).then(function (cache) {&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug) {&lt;br /&gt;
                        if (!cache[slug]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = cache[slug] || null;&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7573</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7573"/>
		<updated>2026-09-28T10:40:57Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The open state for an annotated WORD - used when the card is anchored to&lt;br /&gt;
   the words themselves, which is the case for a commentary&#039;s notes (they&lt;br /&gt;
   carry no superscript). It must not reverse the text out, or the word it&lt;br /&gt;
   is meant to point at becomes unreadable. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight.gr-note-hl-open {&lt;br /&gt;
  background: #fdefe6;&lt;br /&gt;
  border-bottom: 1px solid #b5451b;&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
  box-shadow: 0 0 0 2px #fdefe6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Footnote panel: one section per source document. The page&#039;s own notes&lt;br /&gt;
   come first, then a section per commentary shown on the page. */&lt;br /&gt;
.gra-footnote-section-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.66), 18px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  margin: 14px 4px 6px; padding-bottom: 4px;&lt;br /&gt;
  border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;br /&gt;
.gra-footnote-section-title:first-child { margin-top: 2px; }&lt;br /&gt;
.gra-footnote-section-own { color: #7a6a60; }&lt;br /&gt;
/* Repeated on the card, for when the heading has scrolled out of view. */&lt;br /&gt;
.gra-footnote-source {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(11px, calc(var(--gr-font-size, 22px) * 0.5), 14px);&lt;br /&gt;
  color: #b5451b; opacity: 0.85; margin-bottom: 2px;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7572</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7572"/>
		<updated>2026-09-28T10:37:48Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    // `null` means &amp;quot;this reader has never chosen&amp;quot;, which is different from&lt;br /&gt;
    // &amp;quot;chose none&amp;quot; - the first is what gets every commentary turned on&lt;br /&gt;
    // below, the second has to stay empty or an unchecked box would come&lt;br /&gt;
    // back on the next visit.&lt;br /&gt;
    var teekaChoiceStored = readLS(LS_TEEKA_KEY);&lt;br /&gt;
    try { JSON.parse(teekaChoiceStored || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    // Default to every commentary the page has. The moola&lt;br /&gt;
                    // is published together with its teekas and a reader&lt;br /&gt;
                    // arriving for the first time should see them, rather&lt;br /&gt;
                    // than an apparently bare text with the commentaries&lt;br /&gt;
                    // hidden behind a control they have not found yet.&lt;br /&gt;
                    if (teekaChoiceStored === null || teekaChoiceStored === undefined) {&lt;br /&gt;
                        availableList.forEach(function (it) {&lt;br /&gt;
                            selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                            (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                        });&lt;br /&gt;
                    }&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-hl-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        // A commentary&#039;s note has no superscript, so the element the card is&lt;br /&gt;
        // anchored to IS the highlighted word. gr-note-marker-open paints a&lt;br /&gt;
        // solid maroon chip with white text - right for a small superscript,&lt;br /&gt;
        // and it made the word itself unreadable. The word gets its own,&lt;br /&gt;
        // quieter open state instead.&lt;br /&gt;
        marker.classList.add(marker.classList.contains(&#039;gr-note-marker&#039;)&lt;br /&gt;
            ? &#039;gr-note-marker-open&#039; : &#039;gr-note-hl-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7571</id>
		<title>MediaWiki:Gadget-GrAnnotations.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Gadget-GrAnnotations.js&amp;diff=7571"/>
		<updated>2026-09-28T10:37:01Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span id=&amp;quot;gra-toggle-badge&amp;quot; aria-live=&amp;quot;polite&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso()};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    // CHANGED: grouped by source. A moola page now also carries the notes&lt;br /&gt;
    // of every commentary shown on it, and flattening them into one list&lt;br /&gt;
    // put a commentary&#039;s notes after the moola&#039;s with nothing saying which&lt;br /&gt;
    // was which - the cards were indistinguishable. Each note is filed&lt;br /&gt;
    // under the heading of the document it came from: the page&#039;s own notes&lt;br /&gt;
    // first, then one section per commentary, in the order the page shows&lt;br /&gt;
    // them. common.js wraps injected notes in .gr-teeka-notes-group with a&lt;br /&gt;
    // .gr-teeka-notes-title, which is where the heading comes from.&lt;br /&gt;
    function groupOf(li) {&lt;br /&gt;
      var wrap = li.closest ? li.closest(&#039;.gr-teeka-notes-group&#039;) : null;&lt;br /&gt;
      if (!wrap) return null;&lt;br /&gt;
      var t = wrap.querySelector(&#039;.gr-teeka-notes-title&#039;);&lt;br /&gt;
      return {&lt;br /&gt;
        slug: wrap.getAttribute(&#039;data-teeka-slug&#039;) || &#039;&#039;,&lt;br /&gt;
        title: t ? t.textContent.trim() : &#039;&#039;&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var sections = [];          // [{key, title, items: []}] in document order&lt;br /&gt;
    var byKey = {};&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var g = groupOf(li);&lt;br /&gt;
      var key = g ? g.slug : &#039;&#039;;&lt;br /&gt;
      if (!byKey[key]) {&lt;br /&gt;
        byKey[key] = { key: key, title: g ? g.title : &#039;&#039;, items: [] };&lt;br /&gt;
        sections.push(byKey[key]);&lt;br /&gt;
      }&lt;br /&gt;
      byKey[key].items.push(li);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    sections.forEach(function (sec) {&lt;br /&gt;
      if (sec.title) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title&amp;quot;&amp;gt;&#039; + esc(sec.title) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      } else if (sections.length &amp;gt; 1) {&lt;br /&gt;
        html += &#039;&amp;lt;div class=&amp;quot;gra-footnote-section-title gra-footnote-section-own&amp;quot;&amp;gt;&#039;&lt;br /&gt;
              + esc(pageOwnNotesLabel()) + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
      }&lt;br /&gt;
      sec.items.forEach(function (li) { html += footnoteCard(li, sec.title); });&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The heading for the page&#039;s own notes, shown only when there is at&lt;br /&gt;
  // least one commentary section to tell them apart from.&lt;br /&gt;
  function pageOwnNotesLabel() {&lt;br /&gt;
    var t = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
    return (t &amp;amp;&amp;amp; t.textContent.trim()) || &#039;This page&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function footnoteCard(li, groupTitle) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      // The commentary&#039;s name is repeated on the card itself, not only on&lt;br /&gt;
      // the section heading: a reader who has scrolled the panel, or who&lt;br /&gt;
      // arrives at a card by clicking a word in the text, sees the card&lt;br /&gt;
      // without its heading in view.&lt;br /&gt;
      return &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (groupTitle ? &#039;&amp;lt;div class=&amp;quot;gra-footnote-source&amp;quot;&amp;gt;&#039;+esc(groupTitle)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  function updateBadge() {&lt;br /&gt;
    var n = _notes.length + _bookmarks.length;&lt;br /&gt;
    var $b = $(&#039;#gra-toggle-badge&#039;);&lt;br /&gt;
    if (!$b.length) return;&lt;br /&gt;
    $b.text(n &amp;gt; 99 ? &#039;99+&#039; : String(n)).css(&#039;display&#039;, n ? &#039;block&#039; : &#039;none&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7570</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7570"/>
		<updated>2026-09-28T07:23:06Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  /* The HEADING face - AdishilaDev. Used only by headings and the few&lt;br /&gt;
     title-like labels listed further down, never by body text. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* The READING face. Adishila leads: this is what moola, bhashya, teeka&lt;br /&gt;
     and every other run of body text is set in. AdishilaDev follows only&lt;br /&gt;
     as a fallback, for the rare glyph the plain face lacks.&lt;br /&gt;
     Gentium used to sit first here, so that IAST came out in one face -&lt;br /&gt;
     but that also made it the first family asked for Devanagari, and any&lt;br /&gt;
     locally-installed &amp;quot;Gentium Book Plus&amp;quot; answers for codepoints outside&lt;br /&gt;
     the @font-face unicode-range, so the shlokas stopped being Adishila.&lt;br /&gt;
     IAST now gets its Gentium from --font-translit, used only in that view.&lt;br /&gt;
     Headings do NOT use this variable; they use --font-deva above. */&lt;br /&gt;
  --font-indic: &#039;Adishila&#039;, &#039;AdishilaDev&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;Adishila&#039;, &#039;AdishilaDev&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7569</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7569"/>
		<updated>2026-09-28T07:17:07Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length &amp;amp;&amp;amp; window.grFootnoteRegistry) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        if (window.grFootnoteRegistry) window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    // `null` means &amp;quot;this reader has never chosen&amp;quot;, which is different from&lt;br /&gt;
    // &amp;quot;chose none&amp;quot; - the first is what gets every commentary turned on&lt;br /&gt;
    // below, the second has to stay empty or an unchecked box would come&lt;br /&gt;
    // back on the next visit.&lt;br /&gt;
    var teekaChoiceStored = readLS(LS_TEEKA_KEY);&lt;br /&gt;
    try { JSON.parse(teekaChoiceStored || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    // Default to every commentary the page has. The moola&lt;br /&gt;
                    // is published together with its teekas and a reader&lt;br /&gt;
                    // arriving for the first time should see them, rather&lt;br /&gt;
                    // than an apparently bare text with the commentaries&lt;br /&gt;
                    // hidden behind a control they have not found yet.&lt;br /&gt;
                    if (teekaChoiceStored === null || teekaChoiceStored === undefined) {&lt;br /&gt;
                        availableList.forEach(function (it) {&lt;br /&gt;
                            selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                            (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                        });&lt;br /&gt;
                    }&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        marker.classList.add(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7568</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7568"/>
		<updated>2026-09-28T06:55:28Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  /* CHANGED: Adishila now LEADS. Gentium used to sit first so that IAST&lt;br /&gt;
     came out in one face, but that also made it the first family asked&lt;br /&gt;
     for Devanagari text - and any locally-installed &amp;quot;Gentium Book Plus&amp;quot;&lt;br /&gt;
     answers for codepoints outside the @font-face unicode-range, so the&lt;br /&gt;
     shlokas stopped being Adishila. IAST keeps its Gentium through&lt;br /&gt;
     --font-translit below, which is applied only in that view. */&lt;br /&gt;
  --font-indic: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  /* Latin-first, for the IAST reading only: there plain letters and&lt;br /&gt;
     accented ones must come from one face or the line looks patched. */&lt;br /&gt;
  --font-translit: &#039;Gentium Book Plus&#039;, &#039;AdishilaDev&#039;, &#039;Adishila&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] [data-deva],&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sticky-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;] .gr-sr-group-name { font-family: var(--font-translit) !important; }&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings take AdishilaDev (--font-deva) rather than the general Indic&lt;br /&gt;
   stack, so the heading face is the same everywhere regardless of what&lt;br /&gt;
   else a page mixes in. */&lt;br /&gt;
#mw-content-text h1, #mw-content-text h2, #mw-content-text h3,&lt;br /&gt;
#mw-content-text h4, #mw-content-text h5,&lt;br /&gt;
.mw-heading h1, .mw-heading h2, .mw-heading h3, .mw-heading h4,&lt;br /&gt;
.teeka-title, .gr-teeka-dropdown-header, .gr-sticky-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  /* CHANGED from 1200. The floating action buttons sit at 9999 and 10200,&lt;br /&gt;
     so the card opened underneath them. */&lt;br /&gt;
  position: fixed; z-index: 10500; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* Footnotes belonging to an interleaved commentary, grouped under its name&lt;br /&gt;
   in the right-hand panel. Shown only where that list is shown. */&lt;br /&gt;
.gr-teeka-notes-group { margin-top: 14px; }&lt;br /&gt;
.gr-teeka-notes-title {&lt;br /&gt;
  font-family: var(--font-deva);&lt;br /&gt;
  font-size: clamp(13px, calc(var(--gr-font-size, 22px) * 0.68), 19px);&lt;br /&gt;
  font-weight: 600; color: #b5451b;&lt;br /&gt;
  padding-bottom: 3px; margin-bottom: 4px; border-bottom: 1px solid #f1e4db;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7567</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7567"/>
		<updated>2026-09-28T06:54:34Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: Undo revision 7566 by Vaishnavi (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) + &#039;  [&#039; + num + &#039;]&#039;;&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        marker.classList.add(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
            n++;&lt;br /&gt;
            if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                hl.nextElementSibling.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            var m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
            m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
            m.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
            m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
            m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
            m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            var num = n;&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(m, note, num); }&lt;br /&gt;
            m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, num); }&lt;br /&gt;
            });&lt;br /&gt;
            // The annotated words open it as well. On a phone the marker is a&lt;br /&gt;
            // small target, and the words are what the reader is looking at.&lt;br /&gt;
            hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7566</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7566"/>
		<updated>2026-09-28T06:54:01Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ── FOOTNOTE REGISTRY ────────────────────────────────────────────────&lt;br /&gt;
   Notes that belong to content injected into this page rather than baked&lt;br /&gt;
   into it - an interleaved commentary&#039;s own footnotes live in ITS&lt;br /&gt;
   document, so nothing on the moola page knew what its highlighted words&lt;br /&gt;
   pointed at. Anything registered here is visible to the note card and to&lt;br /&gt;
   the right-hand panel, grouped under the commentary it came from. */&lt;br /&gt;
window.grFootnoteRegistry = (function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var byId = {};            // gra-id -&amp;gt; {text, quote, type, group, groupTitle}&lt;br /&gt;
    var groups = [];          // [{slug, title}] in the order they were added&lt;br /&gt;
    var listeners = [];&lt;br /&gt;
    var pending = null;&lt;br /&gt;
    // Deferred by a tick on purpose. Notes are registered while a&lt;br /&gt;
    // commentary&#039;s block is still being built, BEFORE it is inserted into&lt;br /&gt;
    // the page - so a listener that re-scans the document synchronously&lt;br /&gt;
    // would run against a DOM that does not contain the highlighted words&lt;br /&gt;
    // yet, and find nothing to wire up. Coalescing to one call after the&lt;br /&gt;
    // current task also means a page with ten commentaries re-scans once&lt;br /&gt;
    // rather than ten times.&lt;br /&gt;
    function notify() {&lt;br /&gt;
        if (pending) return;&lt;br /&gt;
        pending = setTimeout(function () {&lt;br /&gt;
            pending = null;&lt;br /&gt;
            listeners.forEach(function (f) { try { f(); } catch (e) {} });&lt;br /&gt;
        }, 0);&lt;br /&gt;
    }&lt;br /&gt;
    return {&lt;br /&gt;
        add: function (slug, title, notes) {&lt;br /&gt;
            if (!groups.some(function (g) { return g.slug === slug; })) {&lt;br /&gt;
                groups.push({ slug: slug, title: title });&lt;br /&gt;
            }&lt;br /&gt;
            var added = false;&lt;br /&gt;
            (notes || []).forEach(function (n) {&lt;br /&gt;
                if (!n || !n.id || byId[n.id]) return;&lt;br /&gt;
                byId[n.id] = {&lt;br /&gt;
                    text: n.text || &#039;&#039;, quote: n.quote || &#039;&#039;,&lt;br /&gt;
                    type: n.kind || n.type || &#039;note&#039;,&lt;br /&gt;
                    group: slug, groupTitle: title&lt;br /&gt;
                };&lt;br /&gt;
                added = true;&lt;br /&gt;
            });&lt;br /&gt;
            if (added) notify();&lt;br /&gt;
        },&lt;br /&gt;
        get: function (id) { return byId[id] || null; },&lt;br /&gt;
        all: function () { return byId; },&lt;br /&gt;
        groups: function () { return groups.slice(); },&lt;br /&gt;
        isInjected: function (id) { return !!byId[id]; },&lt;br /&gt;
        clearInjected: function () { byId = {}; groups = []; notify(); },&lt;br /&gt;
        onChange: function (f) { listeners.push(f); }&lt;br /&gt;
    };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    // join(&#039;\n&#039;) - each entry of `lines` is a printed line,&lt;br /&gt;
                    // and joining with a space ran a two-pada shloka into&lt;br /&gt;
                    // one. The .shloka-line markup inside already carries&lt;br /&gt;
                    // the break where the converter put one; this only&lt;br /&gt;
                    // matters for older documents that have none.&lt;br /&gt;
                    text: (b.lines || []).join(&#039;\n&#039;),&lt;br /&gt;
                    footnotes: b.footnotes || [],&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                // A teeka&#039;s footnotes live in ITS document, not in this&lt;br /&gt;
                // page&#039;s own footnote list - so the highlighted words came&lt;br /&gt;
                // through the injection but nothing on this page knew what&lt;br /&gt;
                // they pointed at, and clicking them did nothing. Register&lt;br /&gt;
                // them here, under the commentary&#039;s title, for the note&lt;br /&gt;
                // card and the right-hand panel to read.&lt;br /&gt;
                if (entry.footnotes &amp;amp;&amp;amp; entry.footnotes.length) {&lt;br /&gt;
                    window.grFootnoteRegistry.add(entry.teekaSlug, entry.teekaTitle, entry.footnotes);&lt;br /&gt;
                }&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() {&lt;br /&gt;
        $(&#039;.gr-injected-teeka-group&#039;).remove();&lt;br /&gt;
        window.grFootnoteRegistry.clearInjected();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    // `null` means &amp;quot;this reader has never chosen&amp;quot;, which is different from&lt;br /&gt;
    // &amp;quot;chose none&amp;quot; - the first is what gets every commentary turned on&lt;br /&gt;
    // below, the second has to stay empty or an unchecked box would come&lt;br /&gt;
    // back on the next visit.&lt;br /&gt;
    var teekaChoiceStored = readLS(LS_TEEKA_KEY);&lt;br /&gt;
    try { JSON.parse(teekaChoiceStored || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    // Default to every commentary the page has. The moola&lt;br /&gt;
                    // is published together with its teekas and a reader&lt;br /&gt;
                    // arriving for the first time should see them, rather&lt;br /&gt;
                    // than an apparently bare text with the commentaries&lt;br /&gt;
                    // hidden behind a control they have not found yet.&lt;br /&gt;
                    if (teekaChoiceStored === null || teekaChoiceStored === undefined) {&lt;br /&gt;
                        availableList.forEach(function (it) {&lt;br /&gt;
                            selectedTeekaSlugs[it.slug] = true;&lt;br /&gt;
                            (it.subItems || []).forEach(function (sub) { selectedTeekaSlugs[sub.slug] = true; });&lt;br /&gt;
                        });&lt;br /&gt;
                    }&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        Object.keys(reg).forEach(function (id) {&lt;br /&gt;
            out[id] = { text: reg[id].text, quote: reg[id].quote,&lt;br /&gt;
                        type: reg[id].type, injected: true,&lt;br /&gt;
                        groupTitle: reg[id].groupTitle };&lt;br /&gt;
        });&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            // An injected note is published into this same list so the&lt;br /&gt;
            // right-hand panel can see it, which means the list entry gets&lt;br /&gt;
            // read back here. Keep the registry&#039;s own record for those, or&lt;br /&gt;
            // the entry overwrites it, the `injected` flag is lost, and the&lt;br /&gt;
            // note is numbered as though it belonged to this document.&lt;br /&gt;
            if (out[id] &amp;amp;&amp;amp; out[id].injected) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        card.addEventListener(&#039;mouseenter&#039;, function () { clearTimeout(hoverTimer); });&lt;br /&gt;
        card.addEventListener(&#039;mouseleave&#039;, function () { if (!COARSE) hoverClose(); });&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) +&lt;br /&gt;
            (typeof num === &#039;number&#039; ? &#039;  [&#039; + num + &#039;]&#039; : (num ? &#039;  \u00b7  &#039; + num : &#039;&#039;));&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        marker.classList.add(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A coarse pointer has no hover, so there the card opens on tap. With a&lt;br /&gt;
    // mouse, hover shows the note and a click is left free for the reader&#039;s&lt;br /&gt;
    // own panel - which is what the marker is for on desktop.&lt;br /&gt;
    var COARSE = (window.matchMedia &amp;amp;&amp;amp; window.matchMedia(&#039;(hover: none), (pointer: coarse)&#039;).matches)&lt;br /&gt;
        || (&#039;ontouchstart&#039; in window) || navigator.maxTouchPoints &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
    var hoverTimer = null;&lt;br /&gt;
    function hoverOpen(m, note, num) {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () { open(m, note, num); }, 90);&lt;br /&gt;
    }&lt;br /&gt;
    function hoverClose() {&lt;br /&gt;
        clearTimeout(hoverTimer);&lt;br /&gt;
        hoverTimer = setTimeout(function () {&lt;br /&gt;
            if (card &amp;amp;&amp;amp; !card.matches(&#039;:hover&#039;)) close();&lt;br /&gt;
        }, 220);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
&lt;br /&gt;
            // An interleaved commentary&#039;s notes get NO superscript. The&lt;br /&gt;
            // numbers would restart per commentary and interleave with the&lt;br /&gt;
            // moola&#039;s own, so &amp;quot;[3]&amp;quot; on the page would mean three different&lt;br /&gt;
            // notes depending on which block it sat in. The highlighted&lt;br /&gt;
            // words stay clickable, and the panel lists them by commentary.&lt;br /&gt;
            var injected = !!note.injected;&lt;br /&gt;
            var num = injected ? null : ++n;&lt;br /&gt;
&lt;br /&gt;
            var m = null;&lt;br /&gt;
            if (!injected) {&lt;br /&gt;
                if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                    hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                    m = hl.nextElementSibling;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                } else {&lt;br /&gt;
                    m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
                    m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
                    m.textContent = &#039;[&#039; + num + &#039;]&#039;;&lt;br /&gt;
                    m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
                    m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
                    hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (hl.dataset.grNoteWired) return;&lt;br /&gt;
            hl.dataset.grNoteWired = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
            var anchorEl = m || hl;&lt;br /&gt;
            var label = injected ? (note.groupTitle || &#039;&#039;) : num;&lt;br /&gt;
&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(anchorEl, note, label); }&lt;br /&gt;
&lt;br /&gt;
            if (COARSE) {&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
                hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Hover opens the card; the click is NOT swallowed, so the&lt;br /&gt;
                // annotation gadget still gets it and opens its Footnotes&lt;br /&gt;
                // panel - the behaviour that was lost when this handler&lt;br /&gt;
                // called stopPropagation on every click.&lt;br /&gt;
                [m, hl].forEach(function (el) {&lt;br /&gt;
                    if (!el) return;&lt;br /&gt;
                    el.addEventListener(&#039;mouseenter&#039;, function () { hoverOpen(anchorEl, note, label); });&lt;br /&gt;
                    el.addEventListener(&#039;mouseleave&#039;, hoverClose);&lt;br /&gt;
                });&lt;br /&gt;
                if (m) m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            }&lt;br /&gt;
            if (m) {&lt;br /&gt;
                m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                    if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, label); }&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    // Commentaries are fetched and injected after the page has rendered, so&lt;br /&gt;
    // their notes arrive later than this module&#039;s first pass. Re-run when&lt;br /&gt;
    // the registry changes, and publish them into the page&#039;s own footnote&lt;br /&gt;
    // list so the right-hand panel - which reads that list, not this&lt;br /&gt;
    // module - shows them too, under the commentary each came from.&lt;br /&gt;
    function publishInjectedList() {&lt;br /&gt;
        var host = document.querySelector(LIST);&lt;br /&gt;
        if (!host) {&lt;br /&gt;
            var content = document.getElementById(&#039;mw-content-text&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            host = document.createElement(&#039;div&#039;);&lt;br /&gt;
            host.className = &#039;gra-qe-footnotes&#039;;&lt;br /&gt;
            content.appendChild(host);&lt;br /&gt;
        }&lt;br /&gt;
        host.querySelectorAll(&#039;.gr-teeka-notes-group&#039;).forEach(function (g) { g.remove(); });&lt;br /&gt;
        var reg = window.grFootnoteRegistry.all();&lt;br /&gt;
        window.grFootnoteRegistry.groups().forEach(function (g) {&lt;br /&gt;
            var ids = Object.keys(reg).filter(function (id) { return reg[id].group === g.slug; });&lt;br /&gt;
            if (!ids.length) return;&lt;br /&gt;
            var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            wrap.className = &#039;gr-teeka-notes-group&#039;;&lt;br /&gt;
            wrap.setAttribute(&#039;data-teeka-slug&#039;, g.slug);&lt;br /&gt;
            var h = document.createElement(&#039;div&#039;);&lt;br /&gt;
            h.className = &#039;gr-teeka-notes-title&#039;;&lt;br /&gt;
            h.textContent = g.title || g.slug;&lt;br /&gt;
            wrap.appendChild(h);&lt;br /&gt;
            var ol = document.createElement(&#039;ol&#039;);&lt;br /&gt;
            ids.forEach(function (id) {&lt;br /&gt;
                var n = reg[id];&lt;br /&gt;
                var li = document.createElement(&#039;li&#039;);&lt;br /&gt;
                li.className = &#039;gra-qe-footnote-item&#039;;&lt;br /&gt;
                li.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
                li.setAttribute(&#039;data-gra-type&#039;, n.type);&lt;br /&gt;
                if (n.quote) {&lt;br /&gt;
                    var q = document.createElement(&#039;span&#039;);&lt;br /&gt;
                    q.className = &#039;gra-qe-footnote-quote&#039;;&lt;br /&gt;
                    q.textContent = n.quote;&lt;br /&gt;
                    li.appendChild(q);&lt;br /&gt;
                }&lt;br /&gt;
                var t = document.createElement(&#039;span&#039;);&lt;br /&gt;
                t.className = &#039;gra-qe-footnote-text&#039;;&lt;br /&gt;
                t.textContent = n.text;&lt;br /&gt;
                li.appendChild(t);&lt;br /&gt;
                ol.appendChild(li);&lt;br /&gt;
            });&lt;br /&gt;
            wrap.appendChild(ol);&lt;br /&gt;
            host.appendChild(wrap);&lt;br /&gt;
        });&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: host } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grFootnoteRegistry.onChange(function () {&lt;br /&gt;
        publishInjectedList();&lt;br /&gt;
        decorate();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7554</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7554"/>
		<updated>2026-09-27T17:48:32Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  --font-indic: &#039;Gentium Book Plus&#039;,&lt;br /&gt;
                &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  position: fixed; z-index: 1200; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
/* The closing verse number is one unit - &amp;quot;॥ ४ ॥&amp;quot; is separated by ordinary&lt;br /&gt;
   spaces, so without this a narrow column wraps between them and strands&lt;br /&gt;
   the number on a line of its own. */&lt;br /&gt;
.gr-verse-num { white-space: nowrap; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7541</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7541"/>
		<updated>2026-09-27T17:28:56Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                // Reading order = the order the book prints the&lt;br /&gt;
                // commentaries in, which a document states as doc.order.&lt;br /&gt;
                // findCommentarySubpages gets its list from&lt;br /&gt;
                // action=query&amp;amp;list=allpages, and that is ALPHABETICAL BY&lt;br /&gt;
                // TITLE - so Vadiraja came before Vyasatirtha, and the&lt;br /&gt;
                // checkbox list, the colour slots and the interleaved&lt;br /&gt;
                // blocks all inherited that same wrong order, since every&lt;br /&gt;
                // one of them is derived from this array. Sorting here&lt;br /&gt;
                // fixes all three at once. A document with no doc.order&lt;br /&gt;
                // (anything published before this field existed) keeps its&lt;br /&gt;
                // existing relative position rather than jumping to the&lt;br /&gt;
                // front, so mixed pages stay stable.&lt;br /&gt;
                pairs = pairs.map(function (p, i) { return { p: p, i: i }; })&lt;br /&gt;
                    .sort(function (a, b) {&lt;br /&gt;
                        var ao = a.p.doc &amp;amp;&amp;amp; a.p.doc.doc &amp;amp;&amp;amp; a.p.doc.doc.order;&lt;br /&gt;
                        var bo = b.p.doc &amp;amp;&amp;amp; b.p.doc.doc &amp;amp;&amp;amp; b.p.doc.doc.order;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo === &#039;number&#039; &amp;amp;&amp;amp; ao !== bo) return ao - bo;&lt;br /&gt;
                        if (typeof ao === &#039;number&#039; &amp;amp;&amp;amp; typeof bo !== &#039;number&#039;) return -1;&lt;br /&gt;
                        if (typeof bo === &#039;number&#039; &amp;amp;&amp;amp; typeof ao !== &#039;number&#039;) return 1;&lt;br /&gt;
                        return a.i - b.i;&lt;br /&gt;
                    })&lt;br /&gt;
                    .map(function (x) { return x.p; });&lt;br /&gt;
&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    // data-gra-type on the &amp;lt;li&amp;gt;. The names the converter writes, and the&lt;br /&gt;
    // ones a QuickEdit button could write later, land in the same map.&lt;br /&gt;
    var TYPES = {&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,   // a variant reading&lt;br /&gt;
        locus: &#039;प्रमाणम्&#039;,          // where a quotation is from — ऋ.सं.७.९९&lt;br /&gt;
        source: &#039;प्रमाणम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;            // an editorial remark&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) + &#039;  [&#039; + num + &#039;]&#039;;&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        marker.classList.add(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
            n++;&lt;br /&gt;
            if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                hl.nextElementSibling.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            var m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
            m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
            m.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
            m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
            m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
            m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            var num = n;&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(m, note, num); }&lt;br /&gt;
            m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, num); }&lt;br /&gt;
            });&lt;br /&gt;
            // The annotated words open it as well. On a phone the marker is a&lt;br /&gt;
            // small target, and the words are what the reader is looking at.&lt;br /&gt;
            hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7540</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7540"/>
		<updated>2026-09-27T17:28:40Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  --font-indic: &#039;Gentium Book Plus&#039;,&lt;br /&gt;
                &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0;&lt;br /&gt;
  font-family: var(--font-indic); }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: var(--font-indic); }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: var(--font-indic)&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  /* CHANGED: fixed 11px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(12px, calc(var(--gr-font-size, 22px) * 0.6), 17px); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(15px, calc(var(--gr-font-size, 22px) * 0.82), 26px);&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  position: fixed; z-index: 1200; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-weight: 600; color: #b5451b;&lt;br /&gt;
  /* CHANGED: fixed 14px -&amp;gt; scales with the reader&#039;s text size. */&lt;br /&gt;
  font-size: clamp(14px, calc(var(--gr-font-size, 22px) * 0.72), 22px);&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); line-height: 1.7; color: #2c1810;&lt;br /&gt;
  /* CHANGED: fixed 17px against 22px body text -&amp;gt; scales with it. */&lt;br /&gt;
  font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.94), 30px);&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: clamp(16px, calc(var(--gr-font-size, 22px) * 0.9), 26px); max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
  .gra-qe-footnote-item,&lt;br /&gt;
  .gra-qe-footnote-text { font-family: var(--font-indic); }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 39. LINE ALIGNMENT CARRIED FROM THE SOURCE DOCUMENT ──────────&lt;br /&gt;
   A converted document puts one of these on a line whose source&lt;br /&gt;
   paragraph carried an explicit alignment (Katha sets 288 paragraphs&lt;br /&gt;
   CENTER - every invocation and colophon). New classes only: no existing&lt;br /&gt;
   rule is overridden, and a line whose source set no alignment gets no&lt;br /&gt;
   class and keeps whatever the site already gives it. */&lt;br /&gt;
.gr-align-center  { text-align: center; }&lt;br /&gt;
.gr-align-right   { text-align: right; }&lt;br /&gt;
.gr-align-justify { text-align: justify; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7527</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7527"/>
		<updated>2026-09-24T18:06:24Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: Undo revision 7526 by Vaishnavi (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    var TYPES = {                       // data-gra-type on the &amp;lt;li&amp;gt;, if set&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,&lt;br /&gt;
        source: &#039;मूलम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) + &#039;  [&#039; + num + &#039;]&#039;;&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        marker.classList.add(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
            n++;&lt;br /&gt;
            if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                hl.nextElementSibling.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            var m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
            m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
            m.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
            m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
            m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
            m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            var num = n;&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(m, note, num); }&lt;br /&gt;
            m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, num); }&lt;br /&gt;
            });&lt;br /&gt;
            // The annotated words open it as well. On a phone the marker is a&lt;br /&gt;
            // small target, and the words are what the reader is looking at.&lt;br /&gt;
            hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7526</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7526"/>
		<updated>2026-09-24T18:04:51Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/**&lt;br /&gt;
 * gr_annotations.js  —  grantha.io inline Notes + Bookmarks + Footnotes + Feedback  (v6 + Strategy B + Footnotes tab)&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* global mw, $ */&lt;br /&gt;
( function () {&lt;br /&gt;
  &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
  var CONTENT_SEL   = &#039;#mw-content-text&#039;;&lt;br /&gt;
  var BM_LS_KEY     = &#039;grantha_bm_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  var NT_LS_KEY     = &#039;grantha_nt_&#039;  + ( ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039; );&lt;br /&gt;
  // Writes that have not reached the server yet. Scoped per user and per&lt;br /&gt;
  // page where it is built: the keys above are per page, and a shared&lt;br /&gt;
  // machine has more than one user.&lt;br /&gt;
  var Q_LS_KEY      = &#039;grantha_q_v1&#039;;&lt;br /&gt;
  var pageTitle     = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var currentUser   = ( window.mw &amp;amp;&amp;amp; mw.config.get( &#039;wgUserName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
  var userInitial   = currentUser ? currentUser.charAt( 0 ).toUpperCase() : &#039;?&#039;;&lt;br /&gt;
  var currentUserEmail = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
  // FIXED: was `new mw.Api()` called directly and synchronously here —&lt;br /&gt;
  // at this point in MediaWiki:Common.js&#039;s own load lifecycle,&lt;br /&gt;
  // mediawiki.api (the module that provides the REAL mw.Api&lt;br /&gt;
  // constructor) is not guaranteed to have finished loading yet, and&lt;br /&gt;
  // calling the constructor before it has throws &amp;quot;mw.Api is not a&lt;br /&gt;
  // constructor&amp;quot; synchronously. Confirmed directly via the real error&lt;br /&gt;
  // trace, and confirmed this is pre-existing code, not anything&lt;br /&gt;
  // introduced by the more recent Ullekha/tab changes. Uncaught here&lt;br /&gt;
  // (the .catch below only ever handled promise rejections, never a&lt;br /&gt;
  // synchronous throw from the constructor call itself), this would&lt;br /&gt;
  // halt the REST of this script&#039;s own execution — meaning later code&lt;br /&gt;
  // in this same file (the tab-building logic) may never run at all on&lt;br /&gt;
  // whatever page load hits this race. mw.loader.using() guarantees the&lt;br /&gt;
  // module is actually ready before mw.Api is ever touched, regardless&lt;br /&gt;
  // of load timing. Verified the exact failure mode and the fix&lt;br /&gt;
  // directly before applying here.&lt;br /&gt;
  if ( currentUser &amp;amp;&amp;amp; window.mw ) {&lt;br /&gt;
    mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
      return new mw.Api().get({ action: &#039;query&#039;, meta: &#039;userinfo&#039;, uiprop: &#039;email&#039;, formatversion: 2 });&lt;br /&gt;
    } ).then( function (data) {&lt;br /&gt;
      var info = data &amp;amp;&amp;amp; data.query &amp;amp;&amp;amp; data.query.userinfo;&lt;br /&gt;
      if ( info &amp;amp;&amp;amp; info.email ) currentUserEmail = info.email;&lt;br /&gt;
    } ).catch( function () {} );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( window.mw ) {&lt;br /&gt;
    var ns = mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
    if ( ns &amp;lt; 0 ) return;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  var _selRange   = null;&lt;br /&gt;
  var _selText    = &#039;&#039;;&lt;br /&gt;
  var _selRect    = null;&lt;br /&gt;
  var _notes      = [];&lt;br /&gt;
  var _bookmarks  = [];&lt;br /&gt;
  var _activeTab  = &#039;notes&#039;;&lt;br /&gt;
  var _selVersion = 0;&lt;br /&gt;
  var _fabSelVer  = -1;&lt;br /&gt;
  var _mobile     = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
  var _fabTouched = false;  // flag to prevent hideActions when tapping fab&lt;br /&gt;
&lt;br /&gt;
  function uid() { return &#039;gra_&#039; + Date.now() + &#039;_&#039; + Math.random().toString(36).slice(2,7); }&lt;br /&gt;
  function esc(s) {&lt;br /&gt;
    return String(s||&#039;&#039;).replace(/&amp;amp;/g,&#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g,&#039;&amp;amp;lt;&#039;)&lt;br /&gt;
                        .replace(/&amp;gt;/g,&#039;&amp;amp;gt;&#039;).replace(/&amp;quot;/g,&#039;&amp;amp;quot;&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function nowIso() { return new Date().toISOString().replace(/\.\d{3}Z$/,&#039;Z&#039;); }&lt;br /&gt;
  function fmtTs(ts) {&lt;br /&gt;
    try {&lt;br /&gt;
      var d = new Date(ts);&lt;br /&gt;
      return d.toLocaleDateString(&#039;en-IN&#039;,{day:&#039;numeric&#039;,month:&#039;short&#039;,year:&#039;numeric&#039;})&lt;br /&gt;
           + &#039; &#039; + d.toLocaleTimeString(&#039;en-IN&#039;,{hour:&#039;2-digit&#039;,minute:&#039;2-digit&#039;,hour12:false});&lt;br /&gt;
    } catch(e){ return ts; }&lt;br /&gt;
  }&lt;br /&gt;
  function clamp(v,lo,hi){ return Math.max(lo,Math.min(hi,v)); }&lt;br /&gt;
  function isMobile() { return _mobile; }&lt;br /&gt;
&lt;br /&gt;
  // The server caps a note at 8192 *bytes* (MAX_TEXT_BYTES), and Devanagari&lt;br /&gt;
  // runs three bytes to the character, so the real limit is about 2,700&lt;br /&gt;
  // characters — reachable for the kind of thing people write here. Caught&lt;br /&gt;
  // at the composer, because the alternative is a note that looks saved,&lt;br /&gt;
  // sits in the panel all session, and is simply absent the next time the&lt;br /&gt;
  // page is opened.&lt;br /&gt;
  var MAX_NOTE_BYTES = 8192;&lt;br /&gt;
  function byteLength(s) {&lt;br /&gt;
    try { return new TextEncoder().encode(s).length; }&lt;br /&gt;
    catch (e) { return unescape(encodeURIComponent(s)).length; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ══════════════════════ Store ══════════════════════&lt;br /&gt;
   * Notes and bookmarks live in the wiki database, in gr_annotations, via&lt;br /&gt;
   * the GranthaAnnotations extension — which is what lets the same notes&lt;br /&gt;
   * show up in the mobile app. They used to live only in localStorage, so&lt;br /&gt;
   * they were stranded in one browser.&lt;br /&gt;
   *&lt;br /&gt;
   * localStorage does not go away; it changes job. It is now&lt;br /&gt;
   *   · a cache,  so a reload paints the panel instantly instead of waiting&lt;br /&gt;
   *               on the network, and a page read offline still shows notes&lt;br /&gt;
   *   · an outbox mirror, so a write made while offline survives a reload&lt;br /&gt;
   * The database is the record. The browser is a convenience in front of it.&lt;br /&gt;
   *&lt;br /&gt;
   * Anonymous readers have no database row to own, and the API refuses them&lt;br /&gt;
   * outright, so for them localStorage stays the store exactly as before.&lt;br /&gt;
   * Every write is optimistic: memory and cache update first, the panel&lt;br /&gt;
   * re-renders immediately, and the request follows.&lt;br /&gt;
   */&lt;br /&gt;
  var Store = ( function () {&lt;br /&gt;
&lt;br /&gt;
    var loggedIn = !!currentUser;&lt;br /&gt;
    // Rejections the server will never change its mind about. Anything not&lt;br /&gt;
    // listed here is treated as temporary and retried.&lt;br /&gt;
    var TERMINAL = { badid: 1, notowner: 1, texttoolong: 1, missingparam: 1 };&lt;br /&gt;
    var _api = null;&lt;br /&gt;
    var flushing = null;          // the in-flight flush&#039;s promise, or null&lt;br /&gt;
    var outbox = [];              // authoritative in memory; mirrored to LS&lt;br /&gt;
&lt;br /&gt;
    // mw.Api only exists once mediawiki.api has loaded, and asking for it&lt;br /&gt;
    // sooner throws. Everything that talks to the server waits on this, so a&lt;br /&gt;
    // load failure degrades to cache-and-queue instead of throwing.&lt;br /&gt;
    function ready() {&lt;br /&gt;
      if ( !window.mw || !mw.loader ) return $.Deferred().reject().promise();&lt;br /&gt;
      return mw.loader.using( &#039;mediawiki.api&#039; ).then( function () {&lt;br /&gt;
        if ( !_api ) _api = new mw.Api();&lt;br /&gt;
        return _api;&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function ls( k, v ) {&lt;br /&gt;
      try {&lt;br /&gt;
        if ( v === undefined ) return localStorage.getItem( k );&lt;br /&gt;
        localStorage.setItem( k, v );&lt;br /&gt;
        return true;&lt;br /&gt;
      } catch ( e ) { return null; }&lt;br /&gt;
    }&lt;br /&gt;
    function readJson( k, fallback ) {&lt;br /&gt;
      try { return JSON.parse( ls( k ) || &#039;&#039; ) || fallback; } catch ( e ) { return fallback; }&lt;br /&gt;
    }&lt;br /&gt;
    function whoami() { return currentUser || null; }&lt;br /&gt;
&lt;br /&gt;
    /* ── Outbox ──────────────────────────────────────────────────────&lt;br /&gt;
     * Held in memory and mirrored to localStorage, not held *in* it. A&lt;br /&gt;
     * browser that refuses to store — private mode, quota, policy — used to&lt;br /&gt;
     * mean the write was never sent at all, because the send re-read the&lt;br /&gt;
     * queue it had just failed to write. Now storage only decides whether an&lt;br /&gt;
     * unsent write survives a reload.&lt;br /&gt;
     *&lt;br /&gt;
     * Keyed per user AND per page: the cache keys have always been per page,&lt;br /&gt;
     * so a queue scoped only by user cannot describe them. */&lt;br /&gt;
    function qKey() { return Q_LS_KEY + &#039;_&#039; + ( currentUser || &#039;anon&#039; ) + &#039;_&#039; + pageTitle; }&lt;br /&gt;
    function mirror() { ls( qKey(), JSON.stringify( outbox ) ); }&lt;br /&gt;
&lt;br /&gt;
    function enqueue( op ) {&lt;br /&gt;
      // One pending operation per annotation. A note edited twice before the&lt;br /&gt;
      // network returns should send its final state once, not both states in&lt;br /&gt;
      // order, and a delete supersedes any save still waiting behind it.&lt;br /&gt;
      outbox = outbox.filter( function ( x ) { return x.id !== op.id; } );&lt;br /&gt;
      outbox.push( op );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
    // Removes the exact operation that was sent, by identity. Matching on&lt;br /&gt;
    // id and op instead would let a completing send delete a *newer*&lt;br /&gt;
    // operation for the same annotation that replaced it mid-flight.&lt;br /&gt;
    function dequeue( op ) {&lt;br /&gt;
      var i = outbox.indexOf( op );&lt;br /&gt;
      if ( i === -1 ) return;&lt;br /&gt;
      outbox.splice( i, 1 );&lt;br /&gt;
      mirror();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function send( op ) {&lt;br /&gt;
      var params = { action: &#039;granthaannotationedit&#039;, op: op.op, id: op.id, format: &#039;json&#039; };&lt;br /&gt;
      if ( op.op === &#039;save&#039; ) {&lt;br /&gt;
        params.page = op.page;&lt;br /&gt;
        params.type = op.type;&lt;br /&gt;
        params.quote = op.quote || &#039;&#039;;&lt;br /&gt;
        params.text = op.text || &#039;&#039;;&lt;br /&gt;
        if ( op.ts ) params.ts = op.ts;&lt;br /&gt;
      }&lt;br /&gt;
      return ready().then( function ( a ) { return a.postWithToken( &#039;csrf&#039;, params ); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Drain the outbox oldest-first, stopping at the first failure so&lt;br /&gt;
     * ordering is preserved and a server that is down is not hammered. */&lt;br /&gt;
    function flush() {&lt;br /&gt;
      if ( !loggedIn ) return $.Deferred().resolve().promise();&lt;br /&gt;
      // Return the flush already running rather than a fresh resolved&lt;br /&gt;
      // promise: load() waits on this before reading, and handing it an&lt;br /&gt;
      // instantly-resolved promise would let the read race the writes.&lt;br /&gt;
      if ( flushing ) return flushing;&lt;br /&gt;
      if ( !outbox.length ) return $.Deferred().resolve().promise();&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      flushing = d.promise();&lt;br /&gt;
      ( function step() {&lt;br /&gt;
        if ( !outbox.length ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
        var op = outbox[ 0 ];&lt;br /&gt;
        send( op ).done( function () {&lt;br /&gt;
          dequeue( op );&lt;br /&gt;
          // Guard against a queue that cannot shrink, which would loop&lt;br /&gt;
          // re-POSTing the same row for ever. Checked by identity, not by&lt;br /&gt;
          // length: a note written while this request was in flight makes&lt;br /&gt;
          // the queue longer, and comparing lengths stopped the drain with&lt;br /&gt;
          // that newer write still unsent.&lt;br /&gt;
          if ( outbox.indexOf( op ) !== -1 ) { flushing = null; d.resolve(); return; }&lt;br /&gt;
          step();&lt;br /&gt;
        } ).fail( function ( code ) {&lt;br /&gt;
          // Verdicts the server will never change its mind about. Left at&lt;br /&gt;
          // the head of the queue they block every later write on this page&lt;br /&gt;
          // for ever, so they are dropped and the drain carries on.&lt;br /&gt;
          if ( TERMINAL[ code ] ) {&lt;br /&gt;
            dequeue( op );&lt;br /&gt;
            step();&lt;br /&gt;
            return;&lt;br /&gt;
          }&lt;br /&gt;
          // Anything else — offline, 5xx, session expired — is temporary.&lt;br /&gt;
          // Stop here so ordering is kept and a struggling server is not&lt;br /&gt;
          // hammered; the next write, `online`, or page load retries.&lt;br /&gt;
          flushing = null;&lt;br /&gt;
          d.resolve();&lt;br /&gt;
        } );&lt;br /&gt;
      }() );&lt;br /&gt;
      return flushing;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function write( op ) {&lt;br /&gt;
      if ( !loggedIn ) return;   // nothing to send, and nothing to accumulate&lt;br /&gt;
      enqueue( op );&lt;br /&gt;
      flush();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── public ────────────────────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
    /* What has been written locally since the last answer from the server.&lt;br /&gt;
     * The outbox alone is not enough to reconcile with: a note written while&lt;br /&gt;
     * the initial read was in flight may well have finished its own POST&lt;br /&gt;
     * first, and would then be gone from the outbox by the time the older&lt;br /&gt;
     * answer arrives — which would take it off the screen and out of the&lt;br /&gt;
     * cache. These two maps outlive the outbox entry and are cleared only&lt;br /&gt;
     * when a read has actually been folded in. */&lt;br /&gt;
    var localSaves = {}, localDeletes = {};&lt;br /&gt;
&lt;br /&gt;
    function save( type, rec ) {&lt;br /&gt;
      localSaves[ rec.id ] = { type: type, rec: rec };&lt;br /&gt;
      delete localDeletes[ rec.id ];&lt;br /&gt;
      write( {&lt;br /&gt;
        op: &#039;save&#039;, id: rec.id, page: pageTitle, type: type,&lt;br /&gt;
        quote: rec.quote || &#039;&#039;, text: type === &#039;bookmark&#039; ? rec.name : rec.text,&lt;br /&gt;
        ts: rec.ts&lt;br /&gt;
      } );&lt;br /&gt;
    }&lt;br /&gt;
    function remove( id ) {&lt;br /&gt;
      localDeletes[ id ] = true;&lt;br /&gt;
      delete localSaves[ id ];&lt;br /&gt;
      write( { op: &#039;delete&#039;, id: id } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Cache what is on screen, so the next load of this page paints from it&lt;br /&gt;
     * before the network answers.&lt;br /&gt;
     *&lt;br /&gt;
     * The cache records whose it is, for both signed-in and anonymous&lt;br /&gt;
     * readers. The keys are per page, not per user, and always have been, so&lt;br /&gt;
     * without an owner two people sharing a machine would see each other&#039;s&lt;br /&gt;
     * notes — and worse, the next person to sign in would have the previous&lt;br /&gt;
     * one&#039;s notes uploaded into their account by migrate(). Recording null&lt;br /&gt;
     * for &amp;quot;nobody was signed in&amp;quot; is what makes those two cases tellable&lt;br /&gt;
     * apart from genuinely pre-upgrade data. */&lt;br /&gt;
    function cache() {&lt;br /&gt;
      ls( NT_LS_KEY, JSON.stringify( { u: whoami(), v: _notes } ) );&lt;br /&gt;
      ls( BM_LS_KEY, JSON.stringify( { u: whoami(), v: _bookmarks } ) );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function cached( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( !raw ) return [];&lt;br /&gt;
      // A bare array predates this version; only migrate() may claim it.&lt;br /&gt;
      if ( Array.isArray( raw ) ) return [];&lt;br /&gt;
      return ( raw.u === whoami() &amp;amp;&amp;amp; Array.isArray( raw.v ) ) ? raw.v : [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fromCache() {&lt;br /&gt;
      _notes = cached( NT_LS_KEY );&lt;br /&gt;
      _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Claiming ────────────────────────────────────────────────────&lt;br /&gt;
     * Hands over whatever this browser holds with no owner: the bare array&lt;br /&gt;
     * the old gadget wrote, and the anonymous cache this one writes. Both&lt;br /&gt;
     * mean &amp;quot;notes taken on this browser with nobody signed in&amp;quot;, which is&lt;br /&gt;
     * what a reader who browsed logged out and then registered has.&lt;br /&gt;
     *&lt;br /&gt;
     * There is no &amp;quot;already migrated&amp;quot; flag, deliberately. A flag has to be&lt;br /&gt;
     * scoped, and every scoping is wrong somewhere: per user alone marked&lt;br /&gt;
     * every page but the first as done; per user and page was set on the&lt;br /&gt;
     * first visit even when there was nothing to hand over, so notes taken&lt;br /&gt;
     * anonymously *afterwards* could never be claimed and were overwritten&lt;br /&gt;
     * on the next sign-in.&lt;br /&gt;
     *&lt;br /&gt;
     * What actually marks the job done is cache(), which replaces the&lt;br /&gt;
     * ownerless value under the same key with an owned one. After a claim&lt;br /&gt;
     * there is nothing left to claim, so a note deleted later on another&lt;br /&gt;
     * device cannot be resurrected by a stale array — and if the browser&lt;br /&gt;
     * refuses to store, re-claiming is harmless anyway, because the ids are&lt;br /&gt;
     * unchanged and the server&#039;s save is an upsert. */&lt;br /&gt;
    /* What a signing-in user may claim as their own: the bare array the old&lt;br /&gt;
     * gadget wrote, and the anonymous cache this one writes. Both mean &amp;quot;this&lt;br /&gt;
     * browser&#039;s notes, with nobody signed in&amp;quot; — which is what someone who&lt;br /&gt;
     * read logged out and then registered has. Without the second case,&lt;br /&gt;
     * signing in read them as unclaimable, then overwrote them with the&lt;br /&gt;
     * server&#039;s empty answer and they were gone for good. */&lt;br /&gt;
    function legacy( key ) {&lt;br /&gt;
      var raw = readJson( key, null );&lt;br /&gt;
      if ( Array.isArray( raw ) ) return raw;&lt;br /&gt;
      if ( raw &amp;amp;&amp;amp; raw.u === null &amp;amp;&amp;amp; Array.isArray( raw.v ) ) return raw.v;&lt;br /&gt;
      return [];&lt;br /&gt;
    }&lt;br /&gt;
    function migrate() {&lt;br /&gt;
      if ( !loggedIn ) return false;&lt;br /&gt;
      var found = false;&lt;br /&gt;
      legacy( NT_LS_KEY ).forEach( function ( n ) {&lt;br /&gt;
        if ( !n || !n.id ) return;&lt;br /&gt;
        // The old gadget imposed no length limit, so a note from before the&lt;br /&gt;
        // upgrade can be past what the server accepts. Left in, it would sit&lt;br /&gt;
        // at the head of the queue; dropped here, it stays on screen and in&lt;br /&gt;
        // the cache, just not synced.&lt;br /&gt;
        if ( byteLength( n.text || &#039;&#039; ) &amp;gt; MAX_NOTE_BYTES ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: n.id, page: pageTitle, type: &#039;note&#039;,&lt;br /&gt;
                   quote: n.quote || &#039;&#039;, text: n.text || &#039;&#039;, ts: n.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      legacy( BM_LS_KEY ).forEach( function ( b ) {&lt;br /&gt;
        if ( !b || !b.id ) return;&lt;br /&gt;
        found = true;&lt;br /&gt;
        enqueue( { op: &#039;save&#039;, id: b.id, page: pageTitle, type: &#039;bookmark&#039;,&lt;br /&gt;
                   quote: b.quote || &#039;&#039;, text: b.name || &#039;&#039;, ts: b.ts } );&lt;br /&gt;
      } );&lt;br /&gt;
      return found;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* ── Reconciliation ──────────────────────────────────────────────&lt;br /&gt;
     * The server&#039;s answer describes the moment the request was sent. A note&lt;br /&gt;
     * written while it was in the air, or a delete, is in the outbox and not&lt;br /&gt;
     * in that answer — replacing state wholesale would take it off the&lt;br /&gt;
     * screen and out of the cache, and the reader would watch their note&lt;br /&gt;
     * disappear. So anything still pending is re-applied on top. */&lt;br /&gt;
    function reconcile( notes, marks ) {&lt;br /&gt;
      function apply( list, type ) {&lt;br /&gt;
        var out = list.filter( function ( r ) {&lt;br /&gt;
          return !localDeletes[ r.id ] &amp;amp;&amp;amp; !localSaves[ r.id ];&lt;br /&gt;
        } );&lt;br /&gt;
        Object.keys( localSaves ).forEach( function ( id ) {&lt;br /&gt;
          var s = localSaves[ id ];&lt;br /&gt;
          if ( s.type === type ) out.push( s.rec );&lt;br /&gt;
        } );&lt;br /&gt;
        return out;&lt;br /&gt;
      }&lt;br /&gt;
      return { notes: apply( notes, &#039;note&#039; ), marks: apply( marks, &#039;bookmark&#039; ) };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Fill _notes and _bookmarks. The cache is in place synchronously so the&lt;br /&gt;
     * first paint is instant; the server&#039;s answer lands later. */&lt;br /&gt;
    function load() {&lt;br /&gt;
      outbox = readJson( qKey(), [] );&lt;br /&gt;
      if ( !Array.isArray( outbox ) ) outbox = [];&lt;br /&gt;
&lt;br /&gt;
      if ( !loggedIn ) {&lt;br /&gt;
        // localStorage is the store, not a cache. Accept the owner-stamped&lt;br /&gt;
        // shape and, once, the bare array the old gadget wrote.&lt;br /&gt;
        _notes = cached( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = cached( BM_LS_KEY );&lt;br /&gt;
        if ( !_notes.length ) _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        if ( !_bookmarks.length ) _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        return $.Deferred().resolve( false ).promise();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( migrate() ) {&lt;br /&gt;
        // Show what this browser already had straight away. migrate() has&lt;br /&gt;
        // just claimed it for whoever is signed in now, so what is on screen&lt;br /&gt;
        // and what is being sent up are the same set — and a first run that&lt;br /&gt;
        // happens to be offline still shows the reader their notes rather&lt;br /&gt;
        // than an empty panel.&lt;br /&gt;
        _notes = legacy( NT_LS_KEY );&lt;br /&gt;
        _bookmarks = legacy( BM_LS_KEY );&lt;br /&gt;
        cache();&lt;br /&gt;
      } else {&lt;br /&gt;
        fromCache();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      var d = $.Deferred();&lt;br /&gt;
      var rows = [];&lt;br /&gt;
&lt;br /&gt;
      // 500 is the server&#039;s maximum page size, and it orders newest first —&lt;br /&gt;
      // so a reader with more than that on one page would silently lose&lt;br /&gt;
      // their *oldest* annotations from the panel and from the cache.&lt;br /&gt;
      // Follow the continuation until it stops rather than assuming nobody&lt;br /&gt;
      // will get there.&lt;br /&gt;
      function fetchFrom( offset ) {&lt;br /&gt;
        return ready().then( function ( a ) {&lt;br /&gt;
          var params = {&lt;br /&gt;
            action: &#039;granthaannotations&#039;, page: pageTitle,&lt;br /&gt;
            limit: 500, formatversion: 2, format: &#039;json&#039;&lt;br /&gt;
          };&lt;br /&gt;
          if ( offset ) params[ &#039;continue&#039; ] = offset;&lt;br /&gt;
          return a.get( params );&lt;br /&gt;
        } ).then( function ( data ) {&lt;br /&gt;
          var block = data &amp;amp;&amp;amp; data.granthaannotations;&lt;br /&gt;
          var got = block &amp;amp;&amp;amp; block.annotations;&lt;br /&gt;
          if ( Array.isArray( got ) ) rows = rows.concat( got );&lt;br /&gt;
          // The module reports its own continuation, inside its own result.&lt;br /&gt;
          var next = block &amp;amp;&amp;amp; block[ &#039;continue&#039; ];&lt;br /&gt;
          // Guard against a server that keeps handing back the same offset.&lt;br /&gt;
          if ( next !== undefined &amp;amp;&amp;amp; next !== null &amp;amp;&amp;amp; String( next ) !== String( offset ) &amp;amp;&amp;amp;&lt;br /&gt;
               rows.length &amp;lt; 5000 ) {&lt;br /&gt;
            return fetchFrom( next );&lt;br /&gt;
          }&lt;br /&gt;
          return rows;&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      flush().always( function () {&lt;br /&gt;
        fetchFrom( null ).done( function () {&lt;br /&gt;
          var notes = [], marks = [];&lt;br /&gt;
          rows.forEach( function ( r ) {&lt;br /&gt;
            if ( r.type === &#039;bookmark&#039; ) {&lt;br /&gt;
              marks.push( { id: r.id, name: r.text, quote: r.quote, ts: r.ts } );&lt;br /&gt;
            } else {&lt;br /&gt;
              notes.push( { id: r.id, ts: r.ts, quote: r.quote, text: r.text } );&lt;br /&gt;
            }&lt;br /&gt;
          } );&lt;br /&gt;
          // The server returns newest first; the panel reverses for display,&lt;br /&gt;
          // so hold them oldest-first the way the local arrays always were.&lt;br /&gt;
          var merged = reconcile( notes.reverse(), marks.reverse() );&lt;br /&gt;
          _notes = merged.notes;&lt;br /&gt;
          _bookmarks = merged.marks;&lt;br /&gt;
          // Folded in, so the local record of what has changed since can&lt;br /&gt;
          // start again. Anything still unsent stays in the outbox.&lt;br /&gt;
          localSaves = {}; localDeletes = {};&lt;br /&gt;
          cache();&lt;br /&gt;
          d.resolve( true );&lt;br /&gt;
        } ).fail( function () {&lt;br /&gt;
          // Offline, or the extension is not installed. The cache already&lt;br /&gt;
          // loaded, and anything written meanwhile is queued.&lt;br /&gt;
          d.resolve( false );&lt;br /&gt;
        } );&lt;br /&gt;
      } );&lt;br /&gt;
      return d.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( loggedIn &amp;amp;&amp;amp; window.addEventListener ) {&lt;br /&gt;
      window.addEventListener( &#039;online&#039;, function () { flush(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return { load: load, save: save, remove: remove, cache: cache, flush: flush,&lt;br /&gt;
             isRemote: function () { return loggedIn; } };&lt;br /&gt;
  }() );&lt;br /&gt;
&lt;br /&gt;
  var $fab, $mobileBar, $panel, $backdrop;&lt;br /&gt;
  var $ntComposer, $ntInput, $ntSubmit;&lt;br /&gt;
  var $bmComposer, $bmInput, $bmSubmit;&lt;br /&gt;
  var $fbComposer, $fbIssueType, $fbText, $fbEmail, $fbSubmit, $fbQuote;&lt;br /&gt;
  // CHANGED: added $tabFootnotes / $paneFootnotes alongside the existing&lt;br /&gt;
  // Notes/Bookmarks tab+pane refs, for the Footnotes tab. REMOVED: the&lt;br /&gt;
  // earlier $tabSandarbha/$paneSandarbha — that feature was replaced by&lt;br /&gt;
  // the real Ullekha citation-index mechanism (see quickedit.js), which&lt;br /&gt;
  // works entirely through common.js&#039;s own site-wide logic and needs no&lt;br /&gt;
  // panel integration here at all.&lt;br /&gt;
  var $tabNotes, $tabBookmarks, $tabFootnotes, $paneNotes, $paneBookmarks, $paneFootnotes;&lt;br /&gt;
&lt;br /&gt;
  function buildDom() {&lt;br /&gt;
    $fab = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-fab&amp;quot; role=&amp;quot;toolbar&amp;quot; aria-label=&amp;quot;Feedback / Notes / Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-note&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Note&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-bookmark&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Mark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Mark&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-feedback&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Feedback&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn&amp;quot; id=&amp;quot;gra-fab-search&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Search&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Search&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;button class=&amp;quot;gra-fab-btn gra-fab-btn-dismiss&amp;quot; id=&amp;quot;gra-fab-dismiss&amp;quot; type=&amp;quot;button&amp;quot; aria-label=&amp;quot;Dismiss&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-fab-btn-label&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fab);&lt;br /&gt;
&lt;br /&gt;
    $mobileBar = $(&#039;&amp;lt;div id=&amp;quot;gra-mobile-bar&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($mobileBar);&lt;br /&gt;
&lt;br /&gt;
    $fbComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-fb-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Send feedback&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-header&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-feedback&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;strong&amp;gt;Feedback&amp;lt;/strong&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-x&amp;quot; id=&amp;quot;gra-fb-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote-label&amp;quot;&amp;gt;Selected text:&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-quote&amp;quot; id=&amp;quot;gra-fb-quote&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Issue type&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;select class=&amp;quot;gra-fb-select&amp;quot; id=&amp;quot;gra-fb-issue&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;— Choose —&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;wrong_text&amp;quot;&amp;gt;Formatting error&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;reference_issue&amp;quot;&amp;gt;Reference issue&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;spelling_mistake&amp;quot;&amp;gt;Spelling mistake&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;option value=&amp;quot;other&amp;quot;&amp;gt;Other&amp;lt;/option&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/select&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Details (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-fb-text&amp;quot; placeholder=&amp;quot;Describe the issue…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-field-label&amp;quot;&amp;gt;Your email (optional)&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input gra-fb-email-input&amp;quot; id=&amp;quot;gra-fb-email&amp;quot; type=&amp;quot;email&amp;quot; placeholder=&amp;quot;you@example.com&amp;quot; autocomplete=&amp;quot;email&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-fb-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-fb-submit&amp;quot; disabled&amp;gt;Send&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-fb-status&amp;quot; id=&amp;quot;gra-fb-status&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($fbComposer);&lt;br /&gt;
&lt;br /&gt;
    $ntComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-composer&amp;quot; id=&amp;quot;gra-nt-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Add note&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-user&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-avatar&amp;quot;&amp;gt;&#039; + esc(currentUser ? userInitial : &#039;✎&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-composer-uname&amp;quot;&amp;gt;&#039; + esc(currentUser || &#039;Notes&#039;) + &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;textarea class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-nt-input&amp;quot; placeholder=&amp;quot;Write a note…&amp;quot; rows=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-nt-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-nt-submit&amp;quot; disabled&amp;gt;Save Note&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($ntComposer);&lt;br /&gt;
&lt;br /&gt;
    $bmComposer = $( [&lt;br /&gt;
      &#039;&amp;lt;div class=&amp;quot;gra-bm-composer&amp;quot; id=&amp;quot;gra-bm-composer&amp;quot; role=&amp;quot;dialog&amp;quot; aria-label=&amp;quot;Bookmark&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-bm-composer-label&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;    Save bookmark&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;input class=&amp;quot;gra-composer-input&amp;quot; id=&amp;quot;gra-bm-input&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Name this bookmark…&amp;quot; autocomplete=&amp;quot;off&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div class=&amp;quot;gra-composer-actions&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-cancel&amp;quot; id=&amp;quot;gra-bm-cancel&amp;quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-btn-submit&amp;quot; id=&amp;quot;gra-bm-submit&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($bmComposer);&lt;br /&gt;
&lt;br /&gt;
    $panel = $( [&lt;br /&gt;
      &#039;&amp;lt;div id=&amp;quot;gra-panel&amp;quot; role=&amp;quot;complementary&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-head&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div id=&amp;quot;gra-panel-title&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button id=&amp;quot;gra-panel-close&amp;quot; title=&amp;quot;Close&amp;quot;&amp;gt;✕&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-tabs&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab gra-tab-active&amp;quot; id=&amp;quot;gra-tab-notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Notes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-bookmarks&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Bookmarks&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;button class=&amp;quot;gra-tab&amp;quot; id=&amp;quot;gra-tab-footnotes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;      &amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; Footnotes&#039;,&lt;br /&gt;
      &#039;    &amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;div id=&amp;quot;gra-panel-body&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane gra-pane-active&amp;quot; id=&amp;quot;gra-pane-notes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-bookmarks&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;    &amp;lt;div class=&amp;quot;gra-pane&amp;quot; id=&amp;quot;gra-pane-footnotes&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($panel);&lt;br /&gt;
&lt;br /&gt;
    $backdrop = $(&#039;&amp;lt;div id=&amp;quot;gra-backdrop&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
    $(&#039;body&#039;).append($backdrop);&lt;br /&gt;
&lt;br /&gt;
    var $toggle = $( [&lt;br /&gt;
      &#039;&amp;lt;button id=&amp;quot;gra-toggle&amp;quot; aria-label=&amp;quot;Notes&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; id=&amp;quot;gra-toggle-icon&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;  &amp;lt;span id=&amp;quot;gra-toggle-badge&amp;quot; aria-live=&amp;quot;polite&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
      &#039;&amp;lt;/button&amp;gt;&#039;,&lt;br /&gt;
    ].join(&#039;&#039;) );&lt;br /&gt;
    $(&#039;body&#039;).append($toggle);&lt;br /&gt;
    $toggle.on(&#039;click&#039;, function() {&lt;br /&gt;
      $panel.hasClass(&#039;gra-panel-open&#039;) ? closePanel() : openPanel(_activeTab);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(&#039;#gra-panel-title&#039;).text(pageTitle.replace(/_/g,&#039; &#039;).split(&#039;/&#039;)[0].slice(0,30));&lt;br /&gt;
    $tabNotes     = $(&#039;#gra-tab-notes&#039;);&lt;br /&gt;
    $tabBookmarks = $(&#039;#gra-tab-bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes = $(&#039;#gra-tab-footnotes&#039;);&lt;br /&gt;
    $paneNotes    = $(&#039;#gra-pane-notes&#039;);&lt;br /&gt;
    $paneBookmarks= $(&#039;#gra-pane-bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes= $(&#039;#gra-pane-footnotes&#039;);&lt;br /&gt;
    $ntInput      = $(&#039;#gra-nt-input&#039;);&lt;br /&gt;
    $ntSubmit     = $(&#039;#gra-nt-submit&#039;);&lt;br /&gt;
    $bmInput      = $(&#039;#gra-bm-input&#039;);&lt;br /&gt;
    $bmSubmit     = $(&#039;#gra-bm-submit&#039;);&lt;br /&gt;
    $fbIssueType  = $(&#039;#gra-fb-issue&#039;);&lt;br /&gt;
    $fbText       = $(&#039;#gra-fb-text&#039;);&lt;br /&gt;
    $fbEmail      = $(&#039;#gra-fb-email&#039;);&lt;br /&gt;
    $fbSubmit     = $(&#039;#gra-fb-submit&#039;);&lt;br /&gt;
    $fbQuote      = $(&#039;#gra-fb-quote&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function captureSelection() {&lt;br /&gt;
    var sel = window.getSelection();&lt;br /&gt;
    if (!sel || sel.isCollapsed || !sel.rangeCount) return false;&lt;br /&gt;
    var range = sel.getRangeAt(0);&lt;br /&gt;
    var text  = sel.toString().trim();&lt;br /&gt;
    if (!text || text.length &amp;lt; 2) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var ancestor = range.commonAncestorContainer;&lt;br /&gt;
    if (ancestor.nodeType === 3) ancestor = ancestor.parentNode;&lt;br /&gt;
    if (!ancestor || !contentEl.contains(ancestor)) return false;&lt;br /&gt;
    var _editorEl = document.getElementById(&#039;se-surface&#039;) ||&lt;br /&gt;
                    document.querySelector(&#039;.se-outer&#039;);&lt;br /&gt;
    if ( _editorEl &amp;amp;&amp;amp; _editorEl.contains(ancestor) ) return false;&lt;br /&gt;
    if ( document.body.classList.contains(&#039;gra-editing&#039;) ) return false;&lt;br /&gt;
    _selText  = text;&lt;br /&gt;
    _selRect  = range.getBoundingClientRect();&lt;br /&gt;
    try { _selRange = range.cloneRange(); }&lt;br /&gt;
    catch(e){ _selRange = null; }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function reCaptureFromDOM() {&lt;br /&gt;
    if (!_selText) return false;&lt;br /&gt;
    var contentEl = document.querySelector(CONTENT_SEL);&lt;br /&gt;
    if (!contentEl) return false;&lt;br /&gt;
    var found = findTextInContent(contentEl, _selText.slice(0,80).replace(/…$/,&#039;&#039;).trim());&lt;br /&gt;
    if (!found) return false;&lt;br /&gt;
    _selRange = found;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function tryShowActions() {&lt;br /&gt;
    if ($fbComposer &amp;amp;&amp;amp; $fbComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($ntComposer &amp;amp;&amp;amp; $ntComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if ($bmComposer &amp;amp;&amp;amp; $bmComposer.hasClass(&#039;gra-composer-visible&#039;)) return;&lt;br /&gt;
    if (!captureSelection()) { hideActions(); return; }&lt;br /&gt;
    _fabSelVer = _selVersion;&lt;br /&gt;
    showFab(_selRect);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFab(rect) {&lt;br /&gt;
    if (!rect) return;&lt;br /&gt;
    var fabW, fabH, top, left;&lt;br /&gt;
    if (_mobile) {&lt;br /&gt;
      $fab.css({ position: &#039;&#039;, top: &#039;&#039;, left: &#039;&#039;, visibility: &#039;&#039; })&lt;br /&gt;
          .addClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    fabW = 46; fabH = 126;&lt;br /&gt;
    top  = rect.top + (rect.height / 2) - (fabH / 2);&lt;br /&gt;
    left = rect.right + 10;&lt;br /&gt;
    if (left + fabW &amp;gt; window.innerWidth - 8) left = rect.left - fabW - 10;&lt;br /&gt;
    top  = clamp(top,  8, window.innerHeight - fabH - 8);&lt;br /&gt;
    left = clamp(left, 8, window.innerWidth  - fabW - 8);&lt;br /&gt;
    $fab.css({ top: top + &#039;px&#039;, left: left + &#039;px&#039; }).addClass(&#039;gra-fab-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function hideFab() { $fab.removeClass(&#039;gra-fab-visible gra-fab-mobile-docked&#039;); }&lt;br /&gt;
  function hideActions() { hideFab(); }&lt;br /&gt;
&lt;br /&gt;
  function wrapSelection(id, cssClass) {&lt;br /&gt;
    var range = _selRange;&lt;br /&gt;
    _selRange = null;&lt;br /&gt;
    if (!range) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      if (!document.contains(range.startContainer) ||&lt;br /&gt;
          !document.contains(range.endContainer)) return null;&lt;br /&gt;
    } catch(e) { return null; }&lt;br /&gt;
    function makeSpan() {&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = cssClass;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, id);&lt;br /&gt;
      return sp;&lt;br /&gt;
    }&lt;br /&gt;
    try {&lt;br /&gt;
      var span = makeSpan();&lt;br /&gt;
      range.surroundContents(span);&lt;br /&gt;
      if (span.parentNode) return span;&lt;br /&gt;
    } catch(e) {}&lt;br /&gt;
    try {&lt;br /&gt;
      var frag = range.extractContents();&lt;br /&gt;
      var sp2  = makeSpan();&lt;br /&gt;
      sp2.appendChild(frag);&lt;br /&gt;
      range.insertNode(sp2);&lt;br /&gt;
      if (sp2 &amp;amp;&amp;amp; sp2.parentNode) return sp2;&lt;br /&gt;
    } catch(e2) {}&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openFeedbackComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    $fbQuote.text(_selText.slice(0,200) + (_selText.length &amp;gt; 200 ? &#039;…&#039; : &#039;&#039;));&lt;br /&gt;
    $fbIssueType.val(&#039;&#039;);&lt;br /&gt;
    $fbText.val(&#039;&#039;);&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    if (currentUserEmail) $fbEmail.val(currentUserEmail);&lt;br /&gt;
    else $fbEmail.val(&#039;&#039;);&lt;br /&gt;
    if (!_mobile) $fbComposer.css({top:&#039;&#039;, left:&#039;&#039;, transform:&#039;&#039;});&lt;br /&gt;
    $fbComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $fbIssueType.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeFeedbackComposer() {&lt;br /&gt;
    $fbComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitFeedback() {&lt;br /&gt;
    var issueType = $fbIssueType.val();&lt;br /&gt;
    var details   = $fbText.val().trim();&lt;br /&gt;
    var email     = $fbEmail.val().trim();&lt;br /&gt;
    var quote     = $fbQuote.text();&lt;br /&gt;
    if (!issueType) return;&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, true).text(&#039;Sending…&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;&#039;).removeClass(&#039;gra-fb-ok gra-fb-err&#039;);&lt;br /&gt;
    var issueLabels = {&lt;br /&gt;
      wrong_text: &#039;Formatting error&#039;, reference_issue: &#039;Reference issue&#039;,&lt;br /&gt;
      spelling_mistake: &#039;Spelling mistake&#039;, other: &#039;Other&#039;&lt;br /&gt;
    };&lt;br /&gt;
    var payload = new FormData();&lt;br /&gt;
    payload.append(&#039;issue_type&#039;,    issueLabels[issueType] || issueType);&lt;br /&gt;
    payload.append(&#039;page&#039;,          pageTitle.replace(/_/g,&#039; &#039;));&lt;br /&gt;
    payload.append(&#039;url&#039;,           window.location.href);&lt;br /&gt;
    payload.append(&#039;selected_text&#039;, quote);&lt;br /&gt;
    payload.append(&#039;details&#039;,       details || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;user_email&#039;,    email || currentUserEmail || &#039;&#039;);&lt;br /&gt;
    payload.append(&#039;wiki_user&#039;,     currentUser || &#039;anonymous&#039;);&lt;br /&gt;
    fetch(&#039;/feedback.php&#039;, {method:&#039;POST&#039;, body:payload})&lt;br /&gt;
      .then(function(r){ return r.json(); })&lt;br /&gt;
      .then(function(data){&lt;br /&gt;
        if (data &amp;amp;&amp;amp; data.ok) showFeedbackSuccess();&lt;br /&gt;
        else showFeedbackError(data &amp;amp;&amp;amp; data.error ? data.error : &#039;Could not send.&#039;);&lt;br /&gt;
      })&lt;br /&gt;
      .catch(function(){ showFeedbackError(&#039;Network error. Please try again.&#039;); });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function showFeedbackSuccess() {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✓ Feedback sent. Thank you!&#039;).addClass(&#039;gra-fb-ok&#039;);&lt;br /&gt;
    setTimeout(closeFeedbackComposer, 2500);&lt;br /&gt;
  }&lt;br /&gt;
  function showFeedbackError(msg) {&lt;br /&gt;
    $fbSubmit.prop(&#039;disabled&#039;, false).text(&#039;Send&#039;);&lt;br /&gt;
    $(&#039;#gra-fb-status&#039;).text(&#039;✗ &#039; + msg).addClass(&#039;gra-fb-err&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openNoteComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $ntComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $ntComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $ntInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeNoteComposer() {&lt;br /&gt;
    $ntComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $ntInput.val(&#039;&#039;);&lt;br /&gt;
    $ntSubmit.prop(&#039;disabled&#039;, true);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitNote() {&lt;br /&gt;
    var text = $ntInput.val().trim();&lt;br /&gt;
    if (!text) return;&lt;br /&gt;
    if (byteLength(text) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This note is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var ts    = nowIso();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-note-highlight&#039;);&lt;br /&gt;
    if (span) span.setAttribute(&#039;data-gra-quote&#039;, quote);&lt;br /&gt;
    var rec = {id:id, ts:ts, quote:quote, text:text};&lt;br /&gt;
    _notes.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;note&#039;, rec);&lt;br /&gt;
    renderNoteCards(); updateBadge();&lt;br /&gt;
    closeNoteComposer();&lt;br /&gt;
    openPanel(&#039;notes&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openBookmarkComposer() {&lt;br /&gt;
    hideActions();&lt;br /&gt;
    if (!_mobile) $bmComposer.css({ top: &#039;&#039;, left: &#039;&#039;, transform: &#039;&#039; });&lt;br /&gt;
    $bmComposer.addClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    setTimeout(function(){ $bmInput.focus(); }, isMobile() ? 300 : 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function closeBookmarkComposer() {&lt;br /&gt;
    $bmComposer.removeClass(&#039;gra-composer-visible&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
    $bmInput.val(&#039;&#039;);&lt;br /&gt;
    _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function submitBookmark() {&lt;br /&gt;
    var name  = $bmInput.val().trim() || (&#039;Bookmark &#039; + (_bookmarks.length+1));&lt;br /&gt;
    // A bookmark&#039;s name is stored in the same column as a note&#039;s text and&lt;br /&gt;
    // is capped the same way.&lt;br /&gt;
    if (byteLength(name) &amp;gt; MAX_NOTE_BYTES) {&lt;br /&gt;
      alert(&#039;This bookmark name is too long to save — please shorten it.&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var id    = uid();&lt;br /&gt;
    var quote = _selText.slice(0,120) + (_selText.length &amp;gt; 120 ? &#039;…&#039; : &#039;&#039;);&lt;br /&gt;
    if (!_selRange &amp;amp;&amp;amp; _selText) reCaptureFromDOM();&lt;br /&gt;
    var span  = wrapSelection(id, &#039;gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span) { span.setAttribute(&#039;data-gra-id&#039;, id); span.setAttribute(&#039;data-gra-name&#039;, name); }&lt;br /&gt;
    var rec = {id:id, name:name, quote:quote, ts:nowIso()};&lt;br /&gt;
    _bookmarks.push(rec);&lt;br /&gt;
    Store.cache();&lt;br /&gt;
    Store.save(&#039;bookmark&#039;, rec);&lt;br /&gt;
    renderBookmarkCards(); updateBadge();&lt;br /&gt;
    closeBookmarkComposer();&lt;br /&gt;
    openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteBookmark(id) {&lt;br /&gt;
    _bookmarks = _bookmarks.filter(function(b){ return b.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-bookmark-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderBookmarkCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function openPanel(tab) {&lt;br /&gt;
    _activeTab = tab || _activeTab;&lt;br /&gt;
    switchTab(_activeTab);&lt;br /&gt;
    $panel.addClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.addClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  function closePanel() {&lt;br /&gt;
    $panel.removeClass(&#039;gra-panel-open&#039;);&lt;br /&gt;
    $backdrop.removeClass(&#039;gra-backdrop-visible&#039;);&lt;br /&gt;
  }&lt;br /&gt;
  // CHANGED: added the footnotes case, alongside notes/bookmarks.&lt;br /&gt;
  function switchTab(tab) {&lt;br /&gt;
    _activeTab = tab;&lt;br /&gt;
    $tabNotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $tabBookmarks.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $tabFootnotes.toggleClass(&#039;gra-tab-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    $paneNotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;notes&#039;);&lt;br /&gt;
    $paneBookmarks.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;bookmarks&#039;);&lt;br /&gt;
    $paneFootnotes.toggleClass(&#039;gra-pane-active&#039;, tab===&#039;footnotes&#039;);&lt;br /&gt;
    if (tab===&#039;notes&#039;) renderNoteCards();&lt;br /&gt;
    else if (tab===&#039;bookmarks&#039;) renderBookmarkCards();&lt;br /&gt;
    else renderFootnoteCards();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderNoteCards() {&lt;br /&gt;
    if (!_notes.length) {&lt;br /&gt;
      $paneNotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No notes yet.&amp;lt;br&amp;gt;Select text and tap ✎ to add one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _notes.slice().reverse().forEach(function(n){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-note-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-header&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-note&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-meta&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + (n.ts ? &#039;&amp;lt;div class=&amp;quot;gra-card-ts&amp;quot;&amp;gt;&#039;+esc(fmtTs(n.ts))+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-note-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(n.id)+&#039;&amp;quot; title=&amp;quot;Delete&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (n.quote ? &#039;&amp;lt;div class=&amp;quot;gra-card-quote&amp;quot;&amp;gt;&#039;+esc(n.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-card-text&amp;quot;&amp;gt;&#039;+esc(n.text)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function deleteNote(id) {&lt;br /&gt;
    _notes = _notes.filter(function(n){ return n.id !== id; });&lt;br /&gt;
    var span = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;].gra-note-highlight&#039;);&lt;br /&gt;
    if (span &amp;amp;&amp;amp; span.parentNode) {&lt;br /&gt;
      var p = span.parentNode;&lt;br /&gt;
      while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
      p.removeChild(span);&lt;br /&gt;
    }&lt;br /&gt;
    Store.cache(); Store.remove(id); renderNoteCards(); updateBadge();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function renderBookmarkCards() {&lt;br /&gt;
    if (!_bookmarks.length) {&lt;br /&gt;
      $paneBookmarks.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No bookmarks yet.&amp;lt;br&amp;gt;Select text and tap 🔖 to save one.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    _bookmarks.slice().reverse().forEach(function(b){&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-bookmark&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(b.name)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (b.quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(b.quote)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;button class=&amp;quot;gra-bookmark-del&amp;quot; data-del-id=&amp;quot;&#039;+esc(b.id)+&#039;&amp;quot; title=&amp;quot;Remove&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // NEW: Footnotes tab — unlike Notes/Bookmarks, footnotes aren&#039;t personal&lt;br /&gt;
  // localStorage annotations, they&#039;re real saved page content (QuickEdit&lt;br /&gt;
  // writes .gra-qe-footnotes/.gra-qe-footnote-item directly into the saved&lt;br /&gt;
  // HTML). So this reads straight off the live DOM on every open, no&lt;br /&gt;
  // fetch, no storage — verified the scan + both-direction lookup logic&lt;br /&gt;
  // against realistic saved-footnote HTML before wiring this in (6/6 pass).&lt;br /&gt;
  function renderFootnoteCards() {&lt;br /&gt;
    var items = document.querySelectorAll(CONTENT_SEL + &#039; .gra-qe-footnote-item&#039;);&lt;br /&gt;
    if (!items.length) {&lt;br /&gt;
      $paneFootnotes.html(&#039;&amp;lt;div class=&amp;quot;gra-empty-state&amp;quot;&amp;gt;No footnotes on this page.&amp;lt;/div&amp;gt;&#039;);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
    var html = &#039;&#039;;&lt;br /&gt;
    Array.prototype.forEach.call(items, function (li) {&lt;br /&gt;
      var numEl = li.querySelector(&#039;.gra-qe-footnote-num&#039;);&lt;br /&gt;
      var quoteEl = li.querySelector(&#039;.gra-qe-footnote-quote&#039;);&lt;br /&gt;
      var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
      var num = numEl ? numEl.textContent.trim() : &#039;&#039;;&lt;br /&gt;
      // CHANGED: was showing the NUMBER as the card&#039;s primary line and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary — nowhere did it show WHICH&lt;br /&gt;
      // text in the document the footnote actually belongs to, which was&lt;br /&gt;
      // the whole point of being able to identify one footnote from&lt;br /&gt;
      // another. Now shows the referenced quote as primary (matching how&lt;br /&gt;
      // Notes/Bookmarks already identify themselves by quote, not by an&lt;br /&gt;
      // arbitrary id), with the number as a small prefix and the&lt;br /&gt;
      // footnote&#039;s own note text as secondary. Falls back to the note&lt;br /&gt;
      // text alone if a footnote predates this change and has no quote&lt;br /&gt;
      // span at all, rather than showing nothing.&lt;br /&gt;
      var quote = quoteEl ? quoteEl.textContent : &#039;&#039;;&lt;br /&gt;
      var text = textEl ? textEl.textContent : (quoteEl ? &#039;&#039; : li.textContent);&lt;br /&gt;
      var primary = quote ? (num + &#039; &#039; + quote) : (num + &#039; &#039; + text);&lt;br /&gt;
      var id = li.getAttribute(&#039;data-gra-id&#039;) || &#039;&#039;;&lt;br /&gt;
      html += &#039;&amp;lt;div class=&amp;quot;gra-bookmark-card gra-footnote-card&amp;quot; data-gra-id=&amp;quot;&#039;+esc(id)+&#039;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-footnote&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-info&amp;quot;&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;div class=&amp;quot;gra-bookmark-name&amp;quot;&amp;gt;&#039;+esc(primary)+&#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + (quote ? &#039;&amp;lt;div class=&amp;quot;gra-bookmark-quote&amp;quot;&amp;gt;&#039;+esc(text)+&#039;&amp;lt;/div&amp;gt;&#039; : &#039;&#039;)&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            + &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
    });&lt;br /&gt;
    $paneFootnotes.html(html);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function scrollToHighlight(id) {&lt;br /&gt;
    var el = document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
    if (!el) return;&lt;br /&gt;
    el.scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;center&#039;});&lt;br /&gt;
    el.classList.add(&#039;gra-hl-active&#039;);&lt;br /&gt;
    setTimeout(function(){ el.classList.remove(&#039;gra-hl-active&#039;); }, 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function wireEvents() {&lt;br /&gt;
&lt;br /&gt;
    /* Suppress native context menu inside article content (Android/desktop) */&lt;br /&gt;
    document.addEventListener(&#039;contextmenu&#039;, function(e) {&lt;br /&gt;
      var tag = e.target.tagName;&lt;br /&gt;
      if (tag === &#039;INPUT&#039; || tag === &#039;TEXTAREA&#039; || tag === &#039;SELECT&#039;) return;&lt;br /&gt;
      var c = document.querySelector(CONTENT_SEL);&lt;br /&gt;
      if (c &amp;amp;&amp;amp; c.contains(e.target)) e.preventDefault();&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
&lt;br /&gt;
    /* Desktop mouseup */&lt;br /&gt;
    $(document).on(&#039;mouseup&#039;, function(e){&lt;br /&gt;
      if (e.button !== 0) return;&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      setTimeout(tryShowActions, 20);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Separate timers so mobile + desktop never clobber each other */&lt;br /&gt;
    var _selTimer    = null;  /* desktop debounce */&lt;br /&gt;
    var _mobShowTimer = null; /* mobile show-on-touchend */&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: show fab quickly after finger lifts (selection settled).&lt;br /&gt;
       180ms feels instant while still letting the range stabilise. */&lt;br /&gt;
    document.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if ($fab[0] &amp;amp;&amp;amp; $fab[0].contains(e.target)) return;&lt;br /&gt;
      clearTimeout(_mobShowTimer);&lt;br /&gt;
      _mobShowTimer = setTimeout(function() {&lt;br /&gt;
        var sel = window.getSelection();&lt;br /&gt;
        if (!sel || sel.isCollapsed || !sel.toString().trim()) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 180);&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Mobile: only HIDE the fab when selection is cleared while it&#039;s visible.&lt;br /&gt;
       (Reposition isn&#039;t needed now that the bar is docked, and re-running&lt;br /&gt;
       showFab here was causing the lag/flicker.) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (!_mobile) return;&lt;br /&gt;
      if (!$fab.hasClass(&#039;gra-fab-visible&#039;)) return;&lt;br /&gt;
      var sel = window.getSelection();&lt;br /&gt;
      if (!sel || sel.isCollapsed || !sel.toString().trim()) {&lt;br /&gt;
        clearTimeout(_mobShowTimer);&lt;br /&gt;
        hideActions();&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* selectionchange debounced (desktop only) */&lt;br /&gt;
    document.addEventListener(&#039;selectionchange&#039;, function() {&lt;br /&gt;
      if (_mobile) return;&lt;br /&gt;
      _selVersion++;&lt;br /&gt;
      clearTimeout(_selTimer);&lt;br /&gt;
      var v = _selVersion;&lt;br /&gt;
      _selTimer = setTimeout(function(){&lt;br /&gt;
        if (v !== _selVersion) return;&lt;br /&gt;
        if (_fabSelVer === v) return;&lt;br /&gt;
        tryShowActions();&lt;br /&gt;
      }, 600);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── KEY FIX: fab touchstart sets flag to prevent hideActions ── */&lt;br /&gt;
    $fab[0].addEventListener(&#039;touchstart&#039;, function(e) {&lt;br /&gt;
      _fabTouched = true;&lt;br /&gt;
      /* Don&#039;t propagate to document handler */&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
    }, { passive: true });&lt;br /&gt;
&lt;br /&gt;
    /* Click outside → hide actions (blocked if fab was touched) */&lt;br /&gt;
    $(document).on(&#039;mousedown touchstart&#039;, function(e){&lt;br /&gt;
      if (_fabTouched) { _fabTouched = false; return; }&lt;br /&gt;
      var t = e.target;&lt;br /&gt;
      if ($fab[0]        &amp;amp;&amp;amp; $fab[0].contains(t))        return;&lt;br /&gt;
      if ($fbComposer[0] &amp;amp;&amp;amp; $fbComposer[0].contains(t)) return;&lt;br /&gt;
      if ($ntComposer[0] &amp;amp;&amp;amp; $ntComposer[0].contains(t)) return;&lt;br /&gt;
      if ($bmComposer[0] &amp;amp;&amp;amp; $bmComposer[0].contains(t)) return;&lt;br /&gt;
      hideActions();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── FAB buttons — use touchend for mobile, click for desktop ── */&lt;br /&gt;
    function fabAction(btnId, action) {&lt;br /&gt;
      var el = document.getElementById(btnId);&lt;br /&gt;
      if (!el) return;&lt;br /&gt;
      /* touchend: fires before document touchstart clears _selRange */&lt;br /&gt;
      el.addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      }, { passive: false });&lt;br /&gt;
      /* click: for desktop */&lt;br /&gt;
      el.addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        e.stopPropagation();&lt;br /&gt;
        if (!_selRange &amp;amp;&amp;amp; !reCaptureFromDOM()) return;&lt;br /&gt;
        action();&lt;br /&gt;
      });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    fabAction(&#039;gra-fab-note&#039;,     openNoteComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-bookmark&#039;, openBookmarkComposer);&lt;br /&gt;
    fabAction(&#039;gra-fab-feedback&#039;, openFeedbackComposer);&lt;br /&gt;
&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;touchend&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
    }, { passive: false });&lt;br /&gt;
    document.getElementById(&#039;gra-fab-search&#039;).addEventListener(&#039;click&#039;, function(e) {&lt;br /&gt;
      e.preventDefault(); e.stopPropagation();&lt;br /&gt;
      var q = _selText;&lt;br /&gt;
      hideActions();&lt;br /&gt;
      _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
      if (q &amp;amp;&amp;amp; window.showSearchDialog) { window.showSearchDialog(q); }&lt;br /&gt;
      else if (q) { $(document).trigger($.Event(&#039;keydown&#039;, {ctrlKey:true, key:&#039;k&#039;, keyCode:75})); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* ── Dismiss button: hide toolbar + clear selection (mobile) ── */&lt;br /&gt;
    (function () {&lt;br /&gt;
      var dismissEl = document.getElementById(&#039;gra-fab-dismiss&#039;);&lt;br /&gt;
      if (!dismissEl) return;&lt;br /&gt;
      function doDismiss(e) {&lt;br /&gt;
        e.preventDefault(); e.stopPropagation();&lt;br /&gt;
        hideActions();&lt;br /&gt;
        _selRange = null; _selText = &#039;&#039;; _selRect = null;&lt;br /&gt;
        if (window.getSelection) {&lt;br /&gt;
          var s = window.getSelection();&lt;br /&gt;
          if (s &amp;amp;&amp;amp; s.removeAllRanges) s.removeAllRanges();&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      dismissEl.addEventListener(&#039;touchend&#039;, doDismiss, { passive: false });&lt;br /&gt;
      dismissEl.addEventListener(&#039;click&#039;, doDismiss);&lt;br /&gt;
    }());&lt;br /&gt;
&lt;br /&gt;
    /* Feedback composer */&lt;br /&gt;
    $fbIssueType.on(&#039;change&#039;, function(){ $fbSubmit.prop(&#039;disabled&#039;, !$(this).val()); });&lt;br /&gt;
    $(&#039;#gra-fb-cancel, #gra-fb-close&#039;).on(&#039;click&#039;, closeFeedbackComposer);&lt;br /&gt;
    $fbSubmit.on(&#039;click&#039;, submitFeedback);&lt;br /&gt;
    $fbText.on(&#039;keydown&#039;, function(e){ if(e.key===&#039;Escape&#039;) closeFeedbackComposer(); });&lt;br /&gt;
&lt;br /&gt;
    /* Note composer */&lt;br /&gt;
    $ntInput.on(&#039;input&#039;, function(){ $ntSubmit.prop(&#039;disabled&#039;, !$(this).val().trim()); });&lt;br /&gt;
    $(&#039;#gra-nt-cancel&#039;).on(&#039;click&#039;, closeNoteComposer);&lt;br /&gt;
    $ntSubmit.on(&#039;click&#039;, submitNote);&lt;br /&gt;
    $ntInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if ((e.ctrlKey||e.metaKey) &amp;amp;&amp;amp; e.key===&#039;Enter&#039;) submitNote();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeNoteComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Bookmark composer */&lt;br /&gt;
    $(&#039;#gra-bm-cancel&#039;).on(&#039;click&#039;, closeBookmarkComposer);&lt;br /&gt;
    $bmSubmit.on(&#039;click&#039;, submitBookmark);&lt;br /&gt;
    $bmInput.on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key===&#039;Enter&#039;) submitBookmark();&lt;br /&gt;
      if (e.key===&#039;Escape&#039;) closeBookmarkComposer();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    /* Panel */&lt;br /&gt;
    $(&#039;#gra-panel-close&#039;).on(&#039;click&#039;, closePanel);&lt;br /&gt;
    $backdrop.on(&#039;click touchend&#039;, function(e){&lt;br /&gt;
      e.preventDefault();&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
    $tabNotes.on(&#039;click&#039;, function(){ switchTab(&#039;notes&#039;); });&lt;br /&gt;
    $tabBookmarks.on(&#039;click&#039;, function(){ switchTab(&#039;bookmarks&#039;); });&lt;br /&gt;
    // NEW: third tab.&lt;br /&gt;
    $tabFootnotes.on(&#039;click&#039;, function(){ switchTab(&#039;footnotes&#039;); });&lt;br /&gt;
&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-note-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneNotes.on(&#039;click&#039;, &#039;.gra-note-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteNote(id);&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-card&#039;, function(e){&lt;br /&gt;
      if ($(e.target).hasClass(&#039;gra-bookmark-del&#039;)) return;&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
    $paneBookmarks.on(&#039;click&#039;, &#039;.gra-bookmark-del&#039;, function(e){&lt;br /&gt;
      e.stopPropagation();&lt;br /&gt;
      var id = $(this).attr(&#039;data-del-id&#039;);&lt;br /&gt;
      if (id) deleteBookmark(id);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: footnote highlights now carry data-gra-id (see quickedit.js&#039;s&lt;br /&gt;
    // redesign — plain highlight span, no href/marker element anymore), so&lt;br /&gt;
    // this can reuse the EXACT same scrollToHighlight(id) helper Notes/&lt;br /&gt;
    // Bookmarks already use below, instead of custom href-parsing logic&lt;br /&gt;
    // that broke every time the marker format changed.&lt;br /&gt;
    $paneFootnotes.on(&#039;click&#039;, &#039;.gra-footnote-card&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      if (id) { closePanel(); scrollToHighlight(id); }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-note-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;notes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneNotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-bookmark-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;bookmarks&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneBookmarks.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        // CHANGED: was missing the gra-card-active flash entirely (only&lt;br /&gt;
        // notes and footnotes had it) — a separate inconsistency from&lt;br /&gt;
        // the CSS gap below. Now matches the other two exactly.&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
    // CHANGED: was .gra-qe-footnote-ref a with e.preventDefault() (it used&lt;br /&gt;
    // to be a real link). Now a plain highlight span, same as notes/&lt;br /&gt;
    // bookmarks — no href to prevent, id read directly via data-gra-id,&lt;br /&gt;
    // and the handler is now identical in shape to the two right above it.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gra-qe-footnote-highlight&#039;, function(){&lt;br /&gt;
      var id = $(this).attr(&#039;data-gra-id&#039;);&lt;br /&gt;
      openPanel(&#039;footnotes&#039;);&lt;br /&gt;
      setTimeout(function(){&lt;br /&gt;
        var $card = $paneFootnotes.find(&#039;[data-gra-id=&amp;quot;&#039;+id+&#039;&amp;quot;]&#039;);&lt;br /&gt;
        if ($card.length) {&lt;br /&gt;
          $card.addClass(&#039;gra-card-active&#039;);&lt;br /&gt;
          $card[0].scrollIntoView({behavior:&#039;smooth&#039;, block:&#039;nearest&#039;});&lt;br /&gt;
          setTimeout(function(){ $card.removeClass(&#039;gra-card-active&#039;); }, 2000);&lt;br /&gt;
        }&lt;br /&gt;
      }, 100);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // NEW: cross-document links from QuickEdit&#039;s Link picker. No real&lt;br /&gt;
    // href is ever saved (confirmed directly: MediaWiki&#039;s sanitizer&lt;br /&gt;
    // escapes &amp;lt;a href&amp;gt; into visible literal text on this wiki, even for&lt;br /&gt;
    // a full absolute URL) — the target lives in data-gr-href instead,&lt;br /&gt;
    // and this handler is what actually performs the navigation.&lt;br /&gt;
    $(CONTENT_SEL).on(&#039;click&#039;, &#039;.gr-crosslink&#039;, function(){&lt;br /&gt;
      var href = $(this).attr(&#039;data-gr-href&#039;);&lt;br /&gt;
      if (!href) return;&lt;br /&gt;
      var newWin = window.open(href, &#039;_blank&#039;);&lt;br /&gt;
      if (newWin) newWin.opener = null; // more reliable cross-browser than relying on window.open&#039;s features string&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $(document).on(&#039;keydown&#039;, function(e){&lt;br /&gt;
      if (e.key !== &#039;Escape&#039;) return;&lt;br /&gt;
      if ($fbComposer.hasClass(&#039;gra-composer-visible&#039;)) closeFeedbackComposer();&lt;br /&gt;
      else if ($ntComposer.hasClass(&#039;gra-composer-visible&#039;)) closeNoteComposer();&lt;br /&gt;
      else if ($bmComposer.hasClass(&#039;gra-composer-visible&#039;)) closeBookmarkComposer();&lt;br /&gt;
      else closePanel();&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Re-anchors note highlights from _notes, the way bookmarks have always&lt;br /&gt;
  // re-anchored from _bookmarks. This used to read a third store —&lt;br /&gt;
  // NT_LS_KEY + &#039;_hl&#039; — holding {id, quote} pairs that _notes already had,&lt;br /&gt;
  // and which nothing kept in step once notes could also arrive from the&lt;br /&gt;
  // server. The old key is left alone rather than deleted; it is simply no&lt;br /&gt;
  // longer consulted.&lt;br /&gt;
  function restoreNoteHighlights() {&lt;br /&gt;
    _notes.forEach(function(h){&lt;br /&gt;
      if (!h.quote || !h.id) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+h.id+&#039;&amp;quot;].gra-note-highlight&#039;)) return;&lt;br /&gt;
      var needle = h.quote.replace(/…$/,&#039;&#039;).trim().slice(0,80);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var range = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!range) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-note-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, h.id);&lt;br /&gt;
      try { range.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function restoreBookmarkHighlights() {&lt;br /&gt;
    _bookmarks.forEach(function(b){&lt;br /&gt;
      if (!b.quote) return;&lt;br /&gt;
      if (document.querySelector(&#039;[data-gra-id=&amp;quot;&#039;+b.id+&#039;&amp;quot;].gra-bookmark-highlight&#039;)) return;&lt;br /&gt;
      var needle = b.quote.replace(/…$/,&#039;&#039;).trim().slice(0,60);&lt;br /&gt;
      if (!needle) return;&lt;br /&gt;
      var found = findTextInContent(document.querySelector(CONTENT_SEL), needle);&lt;br /&gt;
      if (!found) return;&lt;br /&gt;
      var sp = document.createElement(&#039;span&#039;);&lt;br /&gt;
      sp.className = &#039;gra-bookmark-highlight&#039;;&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-id&#039;, b.id);&lt;br /&gt;
      sp.setAttribute(&#039;data-gra-name&#039;, b.name);&lt;br /&gt;
      try { found.surroundContents(sp); } catch(e){}&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function findTextInContent(root, needle) {&lt;br /&gt;
    if (!root || !needle) return null;&lt;br /&gt;
    var text = root.textContent || &#039;&#039;;&lt;br /&gt;
    var idx  = text.indexOf(needle);&lt;br /&gt;
    if (idx &amp;lt; 0) return null;&lt;br /&gt;
    var iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT, null, false);&lt;br /&gt;
    var pos = 0, node, startNode, startOffset, endNode, endOffset;&lt;br /&gt;
    while ((node = iter.nextNode())) {&lt;br /&gt;
      var len = node.nodeValue.length;&lt;br /&gt;
      if (!startNode &amp;amp;&amp;amp; pos + len &amp;gt; idx) { startNode = node; startOffset = idx - pos; }&lt;br /&gt;
      var endIdx = idx + needle.length;&lt;br /&gt;
      if (startNode &amp;amp;&amp;amp; pos + len &amp;gt;= endIdx) { endNode = node; endOffset = endIdx - pos; break; }&lt;br /&gt;
      pos += len;&lt;br /&gt;
    }&lt;br /&gt;
    if (!startNode || !endNode) return null;&lt;br /&gt;
    try {&lt;br /&gt;
      var r = document.createRange();&lt;br /&gt;
      r.setStart(startNode, startOffset);&lt;br /&gt;
      r.setEnd(endNode, endOffset);&lt;br /&gt;
      return r;&lt;br /&gt;
    } catch(e){ return null; }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // REMOVED: injectAnnotationCss(), which JS-injected a defensive&lt;br /&gt;
  // .gra-card-active/.gra-hl-active rule here as a guess, since the real&lt;br /&gt;
  // gr_annotations.css wasn&#039;t available at the time. Now that it is,&lt;br /&gt;
  // confirmed precisely: .gra-hl-active already had working CSS for&lt;br /&gt;
  // notes and footnotes (that JS rule was unneeded there, though it did&lt;br /&gt;
  // fill a real gap for bookmarks&#039; .gra-hl-active, which had none at&lt;br /&gt;
  // all) — and .gra-card-active genuinely only had a rule for&lt;br /&gt;
  // .gra-note-card, nothing for bookmark or footnote cards, confirming&lt;br /&gt;
  // that diagnosis exactly. The JS version also risked overriding the&lt;br /&gt;
  // existing, working note-card style with !important. Fixed directly&lt;br /&gt;
  // in gr_annotations.css instead — see that file.&lt;br /&gt;
&lt;br /&gt;
  $(function() {&lt;br /&gt;
    _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function(){&lt;br /&gt;
      _mobile = window.innerWidth &amp;lt; 768 || &#039;ontouchstart&#039; in window;&lt;br /&gt;
    });&lt;br /&gt;
    buildDom();&lt;br /&gt;
    wireEvents();&lt;br /&gt;
&lt;br /&gt;
    function paint() {&lt;br /&gt;
      try { restoreNoteHighlights(); } catch(e){}&lt;br /&gt;
      try { restoreBookmarkHighlights(); } catch(e){}&lt;br /&gt;
      if ($panel.hasClass(&#039;gra-panel-open&#039;)) switchTab(_activeTab);&lt;br /&gt;
      updateBadge();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The cache is in place the moment load() returns, so the first paint&lt;br /&gt;
    // still happens on the old 500 ms timer and feels unchanged. The&lt;br /&gt;
    // server&#039;s answer arrives later and paints again — highlights it adds&lt;br /&gt;
    // are skipped if already present, so the second pass only fills in what&lt;br /&gt;
    // the cache did not have: a note made on the phone, or in another&lt;br /&gt;
    // browser.&lt;br /&gt;
    var loaded = Store.load();&lt;br /&gt;
    setTimeout(paint, 500);&lt;br /&gt;
    loaded.always(paint);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Small count on the toggle, so a reader can tell there is something in&lt;br /&gt;
  // the panel without opening it — worth having now that notes can arrive&lt;br /&gt;
  // from another device.&lt;br /&gt;
  function updateBadge() {&lt;br /&gt;
    var n = _notes.length + _bookmarks.length;&lt;br /&gt;
    var $b = $(&#039;#gra-toggle-badge&#039;);&lt;br /&gt;
    if (!$b.length) return;&lt;br /&gt;
    $b.text(n &amp;gt; 99 ? &#039;99+&#039; : String(n)).css(&#039;display&#039;, n ? &#039;block&#039; : &#039;none&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7525</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7525"/>
		<updated>2026-09-24T01:46:02Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  --font-indic: &#039;Gentium Book Plus&#039;,&lt;br /&gt;
                &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0; }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;;, sans-serif !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: &#039;AdishilaDev&#039;;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: &#039;AdishilaDev&#039;; }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: &#039;AdishilaDev&#039;&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  font-size: 14px; color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;br /&gt;
&lt;br /&gt;
/* ── 38. FOOTNOTE MARKERS AND CARDS ────────────────────────────── */&lt;br /&gt;
/* QuickEdit writes the notes; common.js turns them into a superscript at the&lt;br /&gt;
   point of reading and a card on click. The list it writes at the foot of the&lt;br /&gt;
   document is taken off the screen but kept for print, where there is nothing&lt;br /&gt;
   to click and the apparatus belongs at the end. */&lt;br /&gt;
&lt;br /&gt;
/* The annotated words. QuickEdit marks them with a flat yellow highlight,&lt;br /&gt;
   which is right while editing and shouts on a reading page — a dotted rule&lt;br /&gt;
   says &amp;quot;there is a note here&amp;quot; without breaking the line of the text. */&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight {&lt;br /&gt;
  background: none;&lt;br /&gt;
  border-bottom: 1px dotted #c9976b;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  cursor: pointer;                 /* the words open the note too, not only the marker */&lt;br /&gt;
}&lt;br /&gt;
#mw-content-text .gra-qe-footnote-highlight:hover { border-bottom-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
.gr-note-marker {&lt;br /&gt;
  display: inline-block; vertical-align: super;&lt;br /&gt;
  margin-left: 1px; padding: 0 3px;&lt;br /&gt;
  font-family: var(--font-ui); font-weight: 600; line-height: 1.5;&lt;br /&gt;
  /* Relative to the text, but capped: the reader&#039;s A+ goes to 30px and an&lt;br /&gt;
     uncapped marker starts competing with the words it annotates. */&lt;br /&gt;
  font-size: clamp(10px, 0.62em, 14px);&lt;br /&gt;
  color: #8a5a2b; background: #fbf1e6; border: 1px solid #eddcc8; border-radius: 4px;&lt;br /&gt;
  cursor: pointer; user-select: none; white-space: nowrap;&lt;br /&gt;
  transition: background 0.12s, color 0.12s, border-color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-marker:hover,&lt;br /&gt;
.gr-note-marker:focus-visible { background: #f3e0c9; color: #6d4318; border-color: #ddc3a3; }&lt;br /&gt;
.gr-note-marker:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }&lt;br /&gt;
.gr-note-marker-open { background: #b5451b; color: #fff !important; border-color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* The card. Fixed, because it is positioned against the marker&#039;s viewport&lt;br /&gt;
   box and follows it on scroll. */&lt;br /&gt;
.gr-note-card {&lt;br /&gt;
  position: fixed; z-index: 1200; width: min(380px, calc(100vw - 20px));&lt;br /&gt;
  background: #fff; border: 1px solid #e7dbd2; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 10px 30px rgba(60, 20, 10, 0.16);&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card[hidden] { display: none; }&lt;br /&gt;
.gr-note-card-head {&lt;br /&gt;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;&lt;br /&gt;
  padding: 9px 10px 9px 14px; border-bottom: 1px solid #f1e4db; background: #fdf8f4;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-kind {&lt;br /&gt;
  font-family: var(--font-indic); font-size: 14px; font-weight: 600; color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close {&lt;br /&gt;
  flex: none; width: 26px; height: 26px; padding: 0;&lt;br /&gt;
  font: 400 18px/1 var(--font-ui); color: #8b7d74;&lt;br /&gt;
  background: transparent; border: 0; border-radius: 6px; cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-note-card-close:hover { background: #f3e7df; color: #5a4a41; }&lt;br /&gt;
.gr-note-card-body {&lt;br /&gt;
  padding: 13px 14px 15px;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 17px; line-height: 1.7; color: #2c1810;&lt;br /&gt;
  max-height: 46vh; overflow-y: auto; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Phones: a sheet at the bottom reads better than a card floating over the&lt;br /&gt;
   line, and there is no hover to fall back on. */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-note-card {&lt;br /&gt;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;&lt;br /&gt;
    width: 100%; border-radius: 14px 14px 0 0; border-bottom: 0;&lt;br /&gt;
    box-shadow: 0 -8px 30px rgba(60, 20, 10, 0.2);&lt;br /&gt;
  }&lt;br /&gt;
  .gr-note-card-body { font-size: 16px; max-height: 50vh; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The list QuickEdit keeps at the foot of the document */&lt;br /&gt;
.gra-qe-footnotes.gr-notes-listed { display: none; }&lt;br /&gt;
@media print {&lt;br /&gt;
  .gr-note-marker { background: none; border: 0; color: #000; }&lt;br /&gt;
  .gr-note-card { display: none !important; }&lt;br /&gt;
  .gra-qe-footnotes.gr-notes-listed { display: block; margin-top: 28px; padding-top: 12px; border-top: 1px solid #ccc; }&lt;br /&gt;
  .gra-qe-footnote-quote { font-weight: 600; margin-right: 6px; }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7524</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7524"/>
		<updated>2026-09-24T01:45:47Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                    // Note markers are [1], [2] … — machinery, not text, and&lt;br /&gt;
                    // the numbers would be transliterated along with the page.&lt;br /&gt;
                    if (p.closest(&#039;.gr-note-marker&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;)&lt;br /&gt;
                                  || p.closest(&#039;.gr-note-marker&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Footnote markers and their cards ────────────────────────────────&lt;br /&gt;
/* QuickEdit already stores footnotes: it wraps the annotated words in&lt;br /&gt;
   &amp;lt;span class=&amp;quot;gra-qe-footnote-highlight&amp;quot; data-gra-id=&amp;quot;…&amp;quot;&amp;gt; and appends a&lt;br /&gt;
   &amp;lt;div class=&amp;quot;gra-qe-footnotes&amp;quot;&amp;gt;&amp;lt;ol&amp;gt; at the foot of the document, one&lt;br /&gt;
   &amp;lt;li data-gra-id=&amp;quot;…&amp;quot;&amp;gt; per note carrying the quoted words and the note.&lt;br /&gt;
&lt;br /&gt;
   That is the whole apparatus, but reading it meant scrolling to the end of&lt;br /&gt;
   a long document and scrolling back. This turns the same data into what&lt;br /&gt;
   Setutila shows: a small superscript after the annotated words, and a card&lt;br /&gt;
   at the point of reading. Nothing is stored differently and QuickEdit is&lt;br /&gt;
   untouched — the list is still written, still saved, and still printed; it&lt;br /&gt;
   is only taken off the screen, where the markers replace it.&lt;br /&gt;
&lt;br /&gt;
   The note text is transliterated with the rest of the page: the card&#039;s body&lt;br /&gt;
   is tagged data-deva and announced through gr-new-content, which the script&lt;br /&gt;
   switcher already listens for. */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var HL = &#039;.gra-qe-footnote-highlight&#039;;&lt;br /&gt;
    var LIST = &#039;.gra-qe-footnotes&#039;;&lt;br /&gt;
    var TYPES = {                       // data-gra-type on the &amp;lt;li&amp;gt;, if set&lt;br /&gt;
        pathantara: &#039;पाठान्तरम्&#039;,&lt;br /&gt;
        source: &#039;मूलम्&#039;,&lt;br /&gt;
        note: &#039;टिप्पणी&#039;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var card = null, openFor = null;&lt;br /&gt;
&lt;br /&gt;
    function notesFromList() {&lt;br /&gt;
        var out = {};&lt;br /&gt;
        document.querySelectorAll(LIST + &#039; .gra-qe-footnote-item&#039;).forEach(function (li) {&lt;br /&gt;
            var id = li.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            if (!id) return;&lt;br /&gt;
            var textEl = li.querySelector(&#039;.gra-qe-footnote-text&#039;);&lt;br /&gt;
            out[id] = {&lt;br /&gt;
                text: (textEl ? textEl.textContent : li.textContent).trim(),&lt;br /&gt;
                quote: (li.querySelector(&#039;.gra-qe-footnote-quote&#039;) || {}).textContent || &#039;&#039;,&lt;br /&gt;
                type: li.getAttribute(&#039;data-gra-type&#039;) || &#039;note&#039;&lt;br /&gt;
            };&lt;br /&gt;
        });&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildCard() {&lt;br /&gt;
        if (card) return card;&lt;br /&gt;
        card = document.createElement(&#039;div&#039;);&lt;br /&gt;
        card.className = &#039;gr-note-card&#039;;&lt;br /&gt;
        card.setAttribute(&#039;role&#039;, &#039;dialog&#039;);&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        card.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;gr-note-card-head&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;gr-note-card-kind&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;gr-note-card-close&amp;quot; aria-label=&amp;quot;Close&amp;quot;&amp;gt;×&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;gr-note-card-body&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-close&#039;).addEventListener(&#039;click&#039;, close);&lt;br /&gt;
        document.body.appendChild(card);&lt;br /&gt;
        return card;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function close() {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        card.hidden = true;&lt;br /&gt;
        if (openFor) {&lt;br /&gt;
            openFor.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            openFor.classList.remove(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        openFor = null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Anchored under the marker, nudged back inside the viewport rather than&lt;br /&gt;
    // flipped: these sit in running text, and a card that jumps above the line&lt;br /&gt;
    // is harder to connect to the word it belongs to than one that shifts left.&lt;br /&gt;
    function place(marker) {&lt;br /&gt;
        var r = marker.getBoundingClientRect();&lt;br /&gt;
        var w = card.offsetWidth, h = card.offsetHeight;&lt;br /&gt;
        var pad = 10;&lt;br /&gt;
        var left = r.left + r.width / 2 - w / 2;&lt;br /&gt;
        left = Math.max(pad, Math.min(left, window.innerWidth - w - pad));&lt;br /&gt;
        var top = r.bottom + 8;&lt;br /&gt;
        if (top + h &amp;gt; window.innerHeight - pad &amp;amp;&amp;amp; r.top - h - 8 &amp;gt; pad) top = r.top - h - 8;&lt;br /&gt;
        card.style.left = Math.round(left) + &#039;px&#039;;&lt;br /&gt;
        card.style.top = Math.round(top) + &#039;px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function open(marker, note, num) {&lt;br /&gt;
        buildCard();&lt;br /&gt;
        if (openFor === marker &amp;amp;&amp;amp; !card.hidden) { close(); return; }&lt;br /&gt;
        close();&lt;br /&gt;
        card.querySelector(&#039;.gr-note-card-kind&#039;).textContent =&lt;br /&gt;
            (TYPES[note.type] || TYPES.note) + &#039;  [&#039; + num + &#039;]&#039;;&lt;br /&gt;
        var body = card.querySelector(&#039;.gr-note-card-body&#039;);&lt;br /&gt;
        // A bare text node, deliberately. gr-new-content&#039;s handler wraps it,&lt;br /&gt;
        // records it as translatable and applies whatever script is showing —&lt;br /&gt;
        // tagging it here instead would make the handler skip it as already&lt;br /&gt;
        // done, and the note would sit in Devanagari on a Telugu page.&lt;br /&gt;
        body.textContent = note.text;&lt;br /&gt;
        card.hidden = false;&lt;br /&gt;
        place(marker);&lt;br /&gt;
        try {&lt;br /&gt;
            window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: body } }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
        marker.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        marker.classList.add(&#039;gr-note-marker-open&#039;);&lt;br /&gt;
        openFor = marker;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var notes = notesFromList();&lt;br /&gt;
        var n = 0;&lt;br /&gt;
        document.querySelectorAll(&#039;#mw-content-text &#039; + HL).forEach(function (hl) {&lt;br /&gt;
            var id = hl.getAttribute(&#039;data-gra-id&#039;);&lt;br /&gt;
            var note = id &amp;amp;&amp;amp; notes[id];&lt;br /&gt;
            if (!note || !note.text) return;          // orphan highlight: leave it alone&lt;br /&gt;
            n++;&lt;br /&gt;
            if (hl.nextElementSibling &amp;amp;&amp;amp;&lt;br /&gt;
                hl.nextElementSibling.classList.contains(&#039;gr-note-marker&#039;)) {&lt;br /&gt;
                hl.nextElementSibling.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            var m = document.createElement(&#039;sup&#039;);&lt;br /&gt;
            m.className = &#039;gr-note-marker&#039;;&lt;br /&gt;
            m.textContent = &#039;[&#039; + n + &#039;]&#039;;&lt;br /&gt;
            m.setAttribute(&#039;role&#039;, &#039;button&#039;);&lt;br /&gt;
            m.setAttribute(&#039;tabindex&#039;, &#039;0&#039;);&lt;br /&gt;
            m.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
            var num = n;&lt;br /&gt;
            function show(e) { e.preventDefault(); e.stopPropagation(); open(m, note, num); }&lt;br /&gt;
            m.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            m.addEventListener(&#039;keydown&#039;, function (e) {&lt;br /&gt;
                if (e.key === &#039;Enter&#039; || e.key === &#039; &#039;) { e.preventDefault(); open(m, note, num); }&lt;br /&gt;
            });&lt;br /&gt;
            // The annotated words open it as well. On a phone the marker is a&lt;br /&gt;
            // small target, and the words are what the reader is looking at.&lt;br /&gt;
            hl.addEventListener(&#039;click&#039;, show);&lt;br /&gt;
            hl.parentNode.insertBefore(m, hl.nextSibling);&lt;br /&gt;
        });&lt;br /&gt;
        var list = document.querySelector(LIST);&lt;br /&gt;
        if (list) list.classList.toggle(&#039;gr-notes-listed&#039;, n &amp;gt; 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
        if (!card || card.hidden) return;&lt;br /&gt;
        if (!card.contains(e.target)) close();&lt;br /&gt;
    });&lt;br /&gt;
    document.addEventListener(&#039;keydown&#039;, function (e) { if (e.key === &#039;Escape&#039;) close(); });&lt;br /&gt;
    window.addEventListener(&#039;resize&#039;, function () { if (openFor) place(openFor); });&lt;br /&gt;
    window.addEventListener(&#039;scroll&#039;, function () { if (openFor) place(openFor); }, true);&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { setTimeout(decorate, 150); });&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7523</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7523"/>
		<updated>2026-09-23T18:54:17Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  --font-indic: &#039;Gentium Book Plus&#039;,&lt;br /&gt;
                &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Every face reports the same 105% above the baseline / 40% below, whatever&lt;br /&gt;
   its own metrics say. That is what keeps the lines on one rhythm: without&lt;br /&gt;
   it each script sat at its own height inside the line box, so a heading&lt;br /&gt;
   jumped a few pixels the moment it was switched.&lt;br /&gt;
&lt;br /&gt;
   How large each script *draws* is handled separately, in §2b, by scaling&lt;br /&gt;
   the text rather than the font file. Doing it there means it still works&lt;br /&gt;
   when a font file is missing or slow — the text is scaled whatever face&lt;br /&gt;
   ends up rendering it — and it leaves one number per script to tune&lt;br /&gt;
   instead of three that have to be kept in proportion.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  ascent-override: 105%; descent-override: 40%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was.&lt;br /&gt;
&lt;br /&gt;
   --gr-script-scale is how much smaller or larger that script has to be&lt;br /&gt;
   drawn to read the same size as Devanagari. Kannada and Telugu draw taller&lt;br /&gt;
   letters at the same font-size, Tamil wider and rounder, Gentium smaller;&lt;br /&gt;
   left alone, switching script looked like the type size had changed.&lt;br /&gt;
   THESE FOUR NUMBERS ARE THE KNOB: if a script still reads a shade large or&lt;br /&gt;
   small next to Adishila on the live site, move its number a percent or two&lt;br /&gt;
   and nothing else. It multiplies whatever size the reader has chosen with&lt;br /&gt;
   A- / A+, so the two controls stay independent. */&lt;br /&gt;
:root                            { --gr-script-scale: 1; }&lt;br /&gt;
html[data-gr-script=&amp;quot;ta&amp;quot;]        { --gr-script-scale: 0.88; }  /* தமிழ்   */&lt;br /&gt;
html[data-gr-script=&amp;quot;kn&amp;quot;]        { --gr-script-scale: 0.86; }  /* ಕನ್ನಡ   */&lt;br /&gt;
html[data-gr-script=&amp;quot;te&amp;quot;]        { --gr-script-scale: 0.88; }  /* తెలుగు  */&lt;br /&gt;
html[data-gr-script=&amp;quot;en&amp;quot;]        { --gr-script-scale: 1.00; }  /* IAST   */&lt;br /&gt;
&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: calc(1em * var(--gr-script-scale));&lt;br /&gt;
}&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
/* The contents panel follows the reader toolbar&#039;s A- / A+ . The toolbar&lt;br /&gt;
   publishes its size as --gr-font-size on &amp;lt;html&amp;gt;; everything below is a&lt;br /&gt;
   ratio of it, so one click moves the body and the contents together. The&lt;br /&gt;
   22px fallback is the toolbar&#039;s own default, for a page without one. */&lt;br /&gt;
.vector-toc {&lt;br /&gt;
  --gr-toc: max(14px, calc(var(--gr-font-size, 22px) * 0.92));&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: var(--gr-toc); color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Each is a ratio of&lt;br /&gt;
   --gr-toc rather than a fixed px, so the levels keep their proportions at&lt;br /&gt;
   every toolbar size; they are told apart by weight and colour as much as&lt;br /&gt;
   by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 1.05) !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.95) !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: calc(var(--gr-toc) * 0.90) !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0; }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;;, sans-serif !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: &#039;AdishilaDev&#039;;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: &#039;AdishilaDev&#039;; }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: &#039;AdishilaDev&#039;&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  font-size: 14px; color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7522</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7522"/>
		<updated>2026-09-23T18:53:49Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    // tagTocEntry, not a re-tag from textContent: an entry&lt;br /&gt;
                    // keeps its Devanagari in a span *inside* .vector-toc-text,&lt;br /&gt;
                    // so .vector-toc-text itself never carries data-deva and a&lt;br /&gt;
                    // :not([data-deva]) sweep matched every entry on every&lt;br /&gt;
                    // fire. It then stored the text as it stood — Tamil, once&lt;br /&gt;
                    // the reader had switched — as that entry&#039;s &amp;quot;original&amp;quot;, so&lt;br /&gt;
                    // the next switch had no Devanagari left to work from and&lt;br /&gt;
                    // the contents panel stayed in the previous script while&lt;br /&gt;
                    // the body moved on. tagTocEntry leaves a tagged entry be.&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7521</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7521"/>
		<updated>2026-09-23T18:36:51Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Which script is on screen, published to CSS. Common.css §2b uses it to&lt;br /&gt;
    // swap the reading area onto --font-indic while a transliteration is&lt;br /&gt;
    // showing: most rules here name Adishila directly, which has no Tamil,&lt;br /&gt;
    // Kannada or Telugu letters, so without this the switched text fell back&lt;br /&gt;
    // to whatever serif the visitor&#039;s machine happened to have — the reason&lt;br /&gt;
    // it looked uneven from one line to the next.&lt;br /&gt;
    function markScriptOnRoot(script) {&lt;br /&gt;
        try {&lt;br /&gt;
            var el = document.documentElement;&lt;br /&gt;
            if (!el) return;&lt;br /&gt;
            if (!script || script === &#039;deva&#039;) el.removeAttribute(&#039;data-gr-script&#039;);&lt;br /&gt;
            else el.setAttribute(&#039;data-gr-script&#039;, script);&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        markScriptOnRoot(script);&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text:not([data-deva])&#039;).forEach(function (span) {&lt;br /&gt;
                        var orig = span.textContent; if (!orig.trim()) return;&lt;br /&gt;
                        span.setAttribute(&#039;data-deva&#039;, orig); translatableSpans.push(span);&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7520</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7520"/>
		<updated>2026-09-23T18:36:26Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate. Every face in it is served from this site (§2a), so&lt;br /&gt;
     a transliterated page looks the same on every machine instead of&lt;br /&gt;
     falling back to whatever the visitor happens to have installed.&lt;br /&gt;
&lt;br /&gt;
     Order matters, not just membership: each face is limited by&lt;br /&gt;
     unicode-range to the characters it should own, so the browser walks&lt;br /&gt;
     the list and the first face that claims the character wins.&lt;br /&gt;
       Gentium Book Plus  → Latin only, i.e. the IAST reading&lt;br /&gt;
       AdishilaDev        → Devanagari, danda, and everything unclaimed&lt;br /&gt;
       Noto Serif Tamil / Kannada / Telugu → their own blocks&lt;br /&gt;
     Gentium sits first because IAST is Latin throughout: if Adishila led,&lt;br /&gt;
     plain letters would come from Adishila and only the accented ones&lt;br /&gt;
     (ā ī ṛ ṣ …) from Gentium, and the line would look patched together. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  --font-indic: &#039;Gentium Book Plus&#039;,&lt;br /&gt;
                &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2a. TRANSLITERATION FONTS ─────────────────────────────────── */&lt;br /&gt;
/* One face per script the reader can switch to, served from this site so&lt;br /&gt;
   the page reads the same everywhere. Subset to the script&#039;s own block and&lt;br /&gt;
   compressed to woff2, so each file is 30–110 KB and only the file for the&lt;br /&gt;
   script actually on screen is ever downloaded.&lt;br /&gt;
&lt;br /&gt;
   Two numbers do the work of making the scripts sit together:&lt;br /&gt;
&lt;br /&gt;
   size-adjust      scales the face so its letters look the same size as&lt;br /&gt;
                    Devanagari at the same font-size. Kannada and Telugu&lt;br /&gt;
                    draw taller letters than Devanagari, Gentium draws&lt;br /&gt;
                    shorter ones, which is why a switched page used to look&lt;br /&gt;
                    like the type size had changed. THIS IS THE KNOB: if a&lt;br /&gt;
                    script still reads a shade large or small next to&lt;br /&gt;
                    Adishila, move it a percent or two, nothing else.&lt;br /&gt;
&lt;br /&gt;
   ascent/descent   fix where the baseline sits inside the line box. Every&lt;br /&gt;
                    face here reports the same 105% above / 40% below, so&lt;br /&gt;
                    lines land on the same rhythm in all five scripts and a&lt;br /&gt;
                    heading no longer jumps a few pixels when switched.&lt;br /&gt;
                    These are written pre-divided by that face&#039;s&lt;br /&gt;
                    size-adjust, because the browser multiplies the two —&lt;br /&gt;
                    change one and the other has to follow.&lt;br /&gt;
&lt;br /&gt;
   Install: put the nine .woff2 files in  /resources/fonts/translit/ . */&lt;br /&gt;
&lt;br /&gt;
/* Tamil — Noto Serif Tamil (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  size-adjust: 90%; ascent-override: 116.7%; descent-override: 44.4%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Tamil&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTamil-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0B80-0BFF, U+200C-200D;&lt;br /&gt;
  size-adjust: 90%; ascent-override: 116.7%; descent-override: 44.4%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kannada — Noto Serif Kannada (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  size-adjust: 88%; ascent-override: 119.3%; descent-override: 45.5%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Kannada&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifKannada-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C80-0CFF, U+200C-200D;&lt;br /&gt;
  size-adjust: 88%; ascent-override: 119.3%; descent-override: 45.5%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Telugu — Noto Serif Telugu (SIL OFL) */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  size-adjust: 92%; ascent-override: 114.1%; descent-override: 43.5%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Serif Telugu&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/NotoSerifTelugu-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0C00-0C7F, U+200C-200D;&lt;br /&gt;
  size-adjust: 92%; ascent-override: 114.1%; descent-override: 43.5%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* IAST — Gentium Book Plus (SIL OFL). Chosen over a general-purpose serif&lt;br /&gt;
   because it draws ṛ ṝ ḷ ḹ ṅ ñ ṭ ḍ ṇ ś ṣ ṃ ḥ m̐ as designed letters rather&lt;br /&gt;
   than stacking a loose accent on a Latin one, which is what made the&lt;br /&gt;
   romanised reading look ragged. The range stops short of U+0964-0965 on&lt;br /&gt;
   purpose: the danda then always comes from the Devanagari face, so it is&lt;br /&gt;
   one shape in every script. */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  size-adjust: 112%; ascent-override: 93.8%; descent-override: 35.7%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 700; font-style: normal; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  size-adjust: 112%; ascent-override: 93.8%; descent-override: 35.7%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Gentium Book Plus&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/translit/GentiumBookPlus-Italic.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
  font-weight: 400; font-style: italic; font-display: swap;&lt;br /&gt;
  unicode-range: U+0000-024F, U+0259, U+0300-036F, U+1E00-1EFF, U+2000-206F, U+20A0-20BF, U+2122;&lt;br /&gt;
  size-adjust: 112%; ascent-override: 93.8%; descent-override: 35.7%; line-gap-override: 0%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2b. APPLYING THEM ─────────────────────────────────────────── */&lt;br /&gt;
/* Most rules below name an Adishila face directly, which is right for&lt;br /&gt;
   Devanagari and useless for anything else: Adishila has no Tamil, Kannada&lt;br /&gt;
   or Telugu letters, so a switched page fell through to the visitor&#039;s&lt;br /&gt;
   default serif — a different font per machine, and often a different one&lt;br /&gt;
   per line. common.js puts the chosen script on &amp;lt;html data-gr-script=&amp;quot;…&amp;quot;&amp;gt;,&lt;br /&gt;
   so while a transliteration is showing, every span the switcher rewrote&lt;br /&gt;
   moves onto --font-indic and picks up the faces above. Devanagari is the&lt;br /&gt;
   no-attribute case and is left exactly as it was. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) [data-deva],&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sticky-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-section-title,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-snippet,&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-sr-group-name {&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* The bold and italic Adishila faces are separate families rather than&lt;br /&gt;
   weights, so a transliterated prateeka or quotation has to ask for the&lt;br /&gt;
   weight it wants instead. */&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-prateeka         { font-weight: 700; }&lt;br /&gt;
html[data-gr-script]:not([data-gr-script=&amp;quot;deva&amp;quot;]) .gr-mulaprateeka-block { font-style: italic; }&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 20px; color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Sizes are in px so&lt;br /&gt;
   they stay readable next to the 22-24px body text; the levels are told&lt;br /&gt;
   apart by weight and colour as much as by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: 21px !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: 19px !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: 18px !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0; }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;;, sans-serif !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: &#039;AdishilaDev&#039;;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: &#039;AdishilaDev&#039;; }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: &#039;AdishilaDev&#039;&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  font-size: 14px; color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7519</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7519"/>
		<updated>2026-09-23T18:19:53Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        watchNewContent();&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var _translitBusy = false;&lt;br /&gt;
    function tagNewContent(container) {&lt;br /&gt;
        if (!container || !container.nodeType) return;&lt;br /&gt;
        _translitBusy = true;&lt;br /&gt;
        try {&lt;br /&gt;
            var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
                if (p.closest &amp;amp;&amp;amp; (p.closest(&#039;.gr-controls&#039;) || p.closest(&#039;.mw-editsection&#039;))) return;&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        } finally { _translitBusy = false; }&lt;br /&gt;
    }&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        tagNewContent(e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Safety net: anything else that adds text to the page later — an&lt;br /&gt;
    // interleaved commentary, a sub-commentary, an edit preview — is picked&lt;br /&gt;
    // up and put into the reader&#039;s chosen script, rather than being left in&lt;br /&gt;
    // Devanagari because it wasn&#039;t there when the page was first tagged.&lt;br /&gt;
    function watchNewContent() {&lt;br /&gt;
        if (!window.MutationObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content || content.getAttribute(&#039;data-gr-translit-watch&#039;)) return;&lt;br /&gt;
        content.setAttribute(&#039;data-gr-translit-watch&#039;, &#039;1&#039;);&lt;br /&gt;
        var queued = [], timer = null;&lt;br /&gt;
        function flush() {&lt;br /&gt;
            timer = null;&lt;br /&gt;
            var items = queued; queued = [];&lt;br /&gt;
            items.forEach(function (n) { if (document.body.contains(n)) tagNewContent(n); });&lt;br /&gt;
        }&lt;br /&gt;
        new MutationObserver(function (muts) {&lt;br /&gt;
            if (_translitBusy) return;&lt;br /&gt;
            muts.forEach(function (m) {&lt;br /&gt;
                Array.prototype.forEach.call(m.addedNodes, function (n) {&lt;br /&gt;
                    var el = n.nodeType === 1 ? n : (n.nodeType === 3 ? n.parentNode : null);&lt;br /&gt;
                    if (!el || el.nodeType !== 1) return;&lt;br /&gt;
                    if (el.hasAttribute &amp;amp;&amp;amp; el.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                    queued.push(el);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
            if (queued.length &amp;amp;&amp;amp; !timer) timer = setTimeout(flush, 60);&lt;br /&gt;
        }).observe(content, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text:not([data-deva])&#039;).forEach(function (span) {&lt;br /&gt;
                        var orig = span.textContent; if (!orig.trim()) return;&lt;br /&gt;
                        span.setAttribute(&#039;data-deva&#039;, orig); translatableSpans.push(span);&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            // Tell the script switcher about the new text, so an interleaved&lt;br /&gt;
            // commentary is transliterated like the rest of the page instead&lt;br /&gt;
            // of staying in Devanagari.&lt;br /&gt;
            try {&lt;br /&gt;
                window.dispatchEvent(new CustomEvent(&#039;gr-new-content&#039;, { detail: { container: $wrap[0] } }));&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7518</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7518"/>
		<updated>2026-09-23T18:13:05Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Section numbers (&amp;quot;2.1.1&amp;quot;) are removed from the contents outright.&lt;br /&gt;
    // Hiding them with CSS was not enough: the script switcher replaces an&lt;br /&gt;
    // entry&#039;s contents with one flat string, which turned the hidden number&lt;br /&gt;
    // into ordinary text — so it reappeared in Tamil, Kannada and the rest.&lt;br /&gt;
    // They are also wrong here, since this contents list leaves entries out.&lt;br /&gt;
    var NUMBERED = /^\s*\d+(?:\.\d+)*[.)]?\s+([\s\S]*\S)\s*$/;&lt;br /&gt;
    function stripTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-numb&#039;), function (n) {&lt;br /&gt;
            if (n.parentNode) n.parentNode.removeChild(n);&lt;br /&gt;
        });&lt;br /&gt;
        // Entries another script already flattened: take the number off the&lt;br /&gt;
        // text, and off the Devanagari original the switcher re-reads.&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-text, .vector-toc-text [data-deva]&#039;), function (el) {&lt;br /&gt;
            var d = el.getAttribute &amp;amp;&amp;amp; el.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (d) { var md = NUMBERED.exec(d); if (md) el.setAttribute(&#039;data-deva&#039;, md[1]); }&lt;br /&gt;
            if (!el.children.length) {&lt;br /&gt;
                var mt = NUMBERED.exec(el.textContent);&lt;br /&gt;
                if (mt) el.textContent = mt[1];&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    // The switcher can rewrite the contents at any time, so watch for it.&lt;br /&gt;
    var _numbObserver = null;&lt;br /&gt;
    function watchTocNumbers() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc || _numbObserver || !window.MutationObserver) return;&lt;br /&gt;
        var pending = false;&lt;br /&gt;
        _numbObserver = new MutationObserver(function () {&lt;br /&gt;
            if (pending) return;&lt;br /&gt;
            pending = true;&lt;br /&gt;
            setTimeout(function () { pending = false; stripTocNumbers(); }, 50);&lt;br /&gt;
        });&lt;br /&gt;
        _numbObserver.observe(toc, { childList: true, subtree: true, characterData: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        stripTocNumbers();&lt;br /&gt;
        watchTocNumbers();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        var container = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container;&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
        nodes.forEach(function (node) {&lt;br /&gt;
            var p = node.parentNode;&lt;br /&gt;
            if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
            var orig = node.textContent;&lt;br /&gt;
            if (!orig.trim()) return;&lt;br /&gt;
            var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
            span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
            p.replaceChild(span, node);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text:not([data-deva])&#039;).forEach(function (span) {&lt;br /&gt;
                        var orig = span.textContent; if (!orig.trim()) return;&lt;br /&gt;
                        span.setAttribute(&#039;data-deva&#039;, orig); translatableSpans.push(span);&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7517</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7517"/>
		<updated>2026-09-23T17:48:21Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A contents entry is &amp;quot;1.2 शीर्षकम्&amp;quot;: the number lives in its own&lt;br /&gt;
    // .vector-toc-numb span, which Common.css hides. Tagging the whole&lt;br /&gt;
    // .vector-toc-text meant transliteration replaced its contents with&lt;br /&gt;
    // one flat string — number included — so switching to Tamil or any&lt;br /&gt;
    // other script made the numbering appear. Only the label is tagged.&lt;br /&gt;
    function tagTocEntry(textEl) {&lt;br /&gt;
        if (!textEl || textEl.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
        var label = textEl.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!label) {&lt;br /&gt;
            label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            label.className = &#039;gr-toc-label&#039;;&lt;br /&gt;
            Array.prototype.slice.call(textEl.childNodes).forEach(function (n) {&lt;br /&gt;
                if (n.nodeType === 1 &amp;amp;&amp;amp; n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;vector-toc-numb&#039;)) return;&lt;br /&gt;
                label.appendChild(n);                 // moves it, order preserved&lt;br /&gt;
            });&lt;br /&gt;
            textEl.appendChild(label);&lt;br /&gt;
        }&lt;br /&gt;
        var orig = label.textContent;&lt;br /&gt;
        if (!orig.trim()) return;&lt;br /&gt;
        if (!label.hasAttribute(&#039;data-deva&#039;)) label.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
        if (translatableSpans.indexOf(label) === -1) translatableSpans.push(label);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(tagTocEntry);&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        var container = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container;&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
        nodes.forEach(function (node) {&lt;br /&gt;
            var p = node.parentNode;&lt;br /&gt;
            if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
            var orig = node.textContent;&lt;br /&gt;
            if (!orig.trim()) return;&lt;br /&gt;
            var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
            span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
            p.replaceChild(span, node);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text:not([data-deva])&#039;).forEach(function (span) {&lt;br /&gt;
                        var orig = span.textContent; if (!orig.trim()) return;&lt;br /&gt;
                        span.setAttribute(&#039;data-deva&#039;, orig); translatableSpans.push(span);&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7516</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.css&amp;diff=7516"/>
		<updated>2026-09-23T13:04:05Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* ═══════════════════════════════════════════════════════════════════&lt;br /&gt;
   MediaWiki:Common.css — Anandamakaranda&lt;br /&gt;
   Clean rebuild — works on Vector (desktop) + Minerva (mobile)&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════════ */&lt;br /&gt;
&lt;br /&gt;
/* ── 1. CSS VARIABLES ──────────────────────────────────────────── */&lt;br /&gt;
:root {&lt;br /&gt;
  --maroon:       #be100e;&lt;br /&gt;
  --orange:       #e65100;&lt;br /&gt;
  --light-orange: #f57c00;&lt;br /&gt;
  --dark-gray:    #555;&lt;br /&gt;
  --light-gray:   #eee;&lt;br /&gt;
  --muted-gray:   #888;&lt;br /&gt;
  --text:         #1a1a1a;&lt;br /&gt;
  --brown:        #a07040;&lt;br /&gt;
  --light-brown:  #e8d4a0;&lt;br /&gt;
&lt;br /&gt;
  /* Font stacks. --font-indic is the one to use for anything the reader&lt;br /&gt;
     can transliterate: it keeps AdishilaDev for Devanagari and falls back&lt;br /&gt;
     to a matching Noto face for Tamil, Kannada, Telugu, Malayalam etc.,&lt;br /&gt;
     so a transliterated heading or TOC entry keeps the same look instead&lt;br /&gt;
     of dropping to the browser&#039;s default font. */&lt;br /&gt;
  --font-deva:  &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  --font-indic: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;,&lt;br /&gt;
                &#039;Noto Serif Tamil&#039;, &#039;Noto Sans Tamil&#039;,&lt;br /&gt;
                &#039;Noto Serif Kannada&#039;, &#039;Noto Sans Kannada&#039;,&lt;br /&gt;
                &#039;Noto Serif Telugu&#039;, &#039;Noto Sans Telugu&#039;,&lt;br /&gt;
                &#039;Noto Serif Malayalam&#039;, &#039;Noto Sans Malayalam&#039;,&lt;br /&gt;
                &#039;Noto Serif Gujarati&#039;, &#039;Noto Sans Gujarati&#039;,&lt;br /&gt;
                &#039;Noto Serif Bengali&#039;, &#039;Noto Sans Bengali&#039;,&lt;br /&gt;
                &#039;Noto Serif Gurmukhi&#039;, &#039;Noto Sans Gurmukhi&#039;,&lt;br /&gt;
                &#039;Noto Serif Oriya&#039;, &#039;Noto Sans Oriya&#039;, serif;&lt;br /&gt;
  --font-ui:    system-ui, -apple-system, &#039;Segoe UI&#039;, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 2. FONTS ──────────────────────────────────────────────────── */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Adishila&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/Adishila.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaBold&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaItalic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/adishila/AdishilaBoldItalic.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: italic;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDev&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDev.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaDevGuru&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaDevGuru.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: bold; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/Noto_Sans_Devanagari/NotoSansDevanagari.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
  src: url(&amp;quot;/resources/fonts/AdishilaVedicSemiBold.ttf&amp;quot;) format(&amp;quot;truetype&amp;quot;);&lt;br /&gt;
  font-weight: normal; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
body {&lt;br /&gt;
 background-color: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── Icon base ──────────────────────────────────────────────────── */&lt;br /&gt;
.gra-icon {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  width:  20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  background-size: contain;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-position: center;&lt;br /&gt;
  flex-shrink: 0;&lt;br /&gt;
}&lt;br /&gt;
/* comment.svg → Feedback (flag/report)  */&lt;br /&gt;
.gra-icon-feedback { background-image: url(&#039;/images/feedback.svg&#039;); }&lt;br /&gt;
/* notes.svg   → Notes                  */&lt;br /&gt;
.gra-icon-note     { background-image: url(&#039;/images/notes.svg&#039;); }&lt;br /&gt;
.gra-icon-bookmark { background-image: url(&#039;/images/bookmark.svg&#039;); }&lt;br /&gt;
.gra-icon-copy     { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.gra-icon-search { background-image: url(&#039;/images/search.svg&#039;); }&lt;br /&gt;
.gra-icon-dismiss { background-image: url(&#039;/images/close.svg&#039;); }&lt;br /&gt;
.gra-icon-prev { background-image: url(&#039;/images/prev.svg&#039;); }&lt;br /&gt;
.gra-icon-next { background-image: url(&#039;/images/next.svg&#039;); }&lt;br /&gt;
.gra-icon-footnote { background-image: url(&#039;/images/footnote.svg&#039;); }&lt;br /&gt;
.gra-icon-sandarbha { background-image: url(&#039;/images/reference.svg&#039;); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#mw-content-text {&lt;br /&gt;
  font-family: &#039;Adishila&#039;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-parser-output {&lt;br /&gt;
  font-size: 22px;&lt;br /&gt;
  line-height: 1.7;&lt;br /&gt;
  color: #555;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Danda marks wrap issue&lt;br /&gt;
.mw-parser-output { overflow-wrap: anywhere; }&lt;br /&gt;
&lt;br /&gt;
/* ── 4. HIDE ELEMENTS (both skins) ────────────────────────────── */&lt;br /&gt;
/* Page title */&lt;br /&gt;
h1.firstHeading, #firstHeading, .mw-page-title-main { display: none !important; }&lt;br /&gt;
/* Footer */&lt;br /&gt;
#footer, .mw-footer, .vector-footer, .vector-footer-container,&lt;br /&gt;
footer, .minerva-footer { display: none !important; }&lt;br /&gt;
/* Categories */&lt;br /&gt;
.catlinks, #catlinks, .mw-normal-catlinks, .mw-hidden-catlinks { display: none !important; }&lt;br /&gt;
/* Edit section links */&lt;br /&gt;
.mw-editsection, .mw-editsection-bracket { display: none !important; }&lt;br /&gt;
/* Site notice */&lt;br /&gt;
#siteNotice { display: none !important; }&lt;br /&gt;
/* Watchlist */&lt;br /&gt;
li#pt-watchlist, li#pt-watchlist-kbd { display: none !important; }&lt;br /&gt;
/* Appearance panel */&lt;br /&gt;
#vector-appearance, #vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container, .mw-portlet-appearance,&lt;br /&gt;
#p-appearance, [aria-controls=&amp;quot;vector-appearance&amp;quot;] { display: none !important; }&lt;br /&gt;
/* Vector hamburger + sidebar */&lt;br /&gt;
#mw-sidebar-button, .vector-main-menu, .vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark, #vector-main-menu-dropdown { display: none !important; }&lt;br /&gt;
/* Vector search box (we use our own) */&lt;br /&gt;
#p-search, .vector-search-box, .vector-search-box-vue,&lt;br /&gt;
.vector-search-box-collapses { display: none !important; }&lt;br /&gt;
/* Vector page toolbar */&lt;br /&gt;
.vector-page-toolbar, .vector-page-toolbar-container { display: none !important; }&lt;br /&gt;
/* Vector page tools */&lt;br /&gt;
.vector-page-tools { display: none !important; }&lt;br /&gt;
/* Empty paragraphs */&lt;br /&gt;
.mw-parser-output p:empty,&lt;br /&gt;
.mw-parser-output &amp;gt; div &amp;gt; p:empty,&lt;br /&gt;
.mw-heading + p:empty,&lt;br /&gt;
.adhyaya-block + p:empty,&lt;br /&gt;
.verse-block + p:empty { display: none !important; margin: 0 !important; }&lt;br /&gt;
/* Empty divs */&lt;br /&gt;
.adhyaya-block:empty, .mw-parser-output div:empty { display: none !important; }&lt;br /&gt;
/* Cargo hidden */&lt;br /&gt;
.cargo-store-only { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 5. VECTOR HEADER (desktop only) ──────────────────────────── */&lt;br /&gt;
.vector-header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  border-bottom: 1px solid rgba(255,255,255,0.12);&lt;br /&gt;
}&lt;br /&gt;
.vector-header {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  justify-content: space-between !important;&lt;br /&gt;
  padding-left: 20px !important;&lt;br /&gt;
  padding-right: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-start {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 12px;&lt;br /&gt;
}&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
  flex: 0 0 auto !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  gap: 16px;&lt;br /&gt;
  margin-left: auto;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo { display: flex !important; align-items: center; opacity: 1 !important; }&lt;br /&gt;
.mw-logo-icon { width: 24px; height: 24px; }&lt;br /&gt;
&lt;br /&gt;
/* Site name */&lt;br /&gt;
.mw-logo-wordmark {&lt;br /&gt;
  font-size: 0 !important;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::before {&lt;br /&gt;
  content: &#039;Anandamakaranda&#039;;&lt;br /&gt;
  font-size: 17px;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
  letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-logo-wordmark::after {&lt;br /&gt;
  content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 11px;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  color: rgba(255,255,255,0.85);&lt;br /&gt;
  letter-spacing: 0.3px;&lt;br /&gt;
  margin-top: 1px;&lt;br /&gt;
  font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Header right links */&lt;br /&gt;
.vector-header-end &amp;gt; *:not(#p-search) { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
.vector-user-links a { color: rgba(255,255,255,0.9) !important; }&lt;br /&gt;
&lt;br /&gt;
/* User icon white */&lt;br /&gt;
.vector-user-menu-container .vector-icon svg,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg path,&lt;br /&gt;
.vector-user-menu-container .vector-icon svg circle {&lt;br /&gt;
  fill: #fff !important; stroke: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg,&lt;br /&gt;
.vector-user-links .vector-user-menu-chevron .vector-icon svg path {&lt;br /&gt;
  fill: rgba(255,255,255,0.9) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* User dropdown — dark text on white card */&lt;br /&gt;
.vector-user-menu .mw-list-item a,&lt;br /&gt;
.vector-user-menu .vector-menu-content-list a,&lt;br /&gt;
#p-personal .vector-menu-content a,&lt;br /&gt;
.mw-portlet-personal .vector-menu-content a { color: #202122 !important; }&lt;br /&gt;
.vector-user-menu .mw-list-item a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress Vector sticky header clone */&lt;br /&gt;
.vector-sticky-header { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Suppress body top padding that Vector adds */&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 6. VECTOR TOC (desktop sidebar) ──────────────────────────── */&lt;br /&gt;
/* Levels are told apart by size, weight and colour; nesting is shown by&lt;br /&gt;
   indentation and a guide line. Every rule below uses the child&lt;br /&gt;
   combinator (&amp;gt;), so highlighting an entry never spills onto the&lt;br /&gt;
   sub-entries underneath it. */&lt;br /&gt;
.vector-toc .vector-toc-numb { display: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-title {&lt;br /&gt;
  font-size: 15px !important; font-weight: 700 !important;&lt;br /&gt;
  text-transform: uppercase; letter-spacing: 0.08em;&lt;br /&gt;
  color: var(--text) !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-text {&lt;br /&gt;
  white-space: normal !important; word-break: break-word !important;&lt;br /&gt;
  overflow-wrap: anywhere !important; display: inline !important;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
}&lt;br /&gt;
/* :not() keeps this off the मूलम् / उल्लेख buttons that sit in the same&lt;br /&gt;
   panel — otherwise this rule reshaped them into flat contents links. */&lt;br /&gt;
.vector-toc a:not(.toc-main-link-item), .vector-toc .vector-toc-link {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: var(--font-indic) !important;&lt;br /&gt;
  font-size: 20px; color: var(--text) !important; text-decoration: none !important;&lt;br /&gt;
  padding: 4px 8px; border-radius: 5px; line-height: 1.45;&lt;br /&gt;
  transition: background 0.12s, color 0.12s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Level 1 = section heading, level 2+ = sub-sections. Sizes are in px so&lt;br /&gt;
   they stay readable next to the 22-24px body text; the levels are told&lt;br /&gt;
   apart by weight and colour as much as by size. */&lt;br /&gt;
.vector-toc .vector-toc-level-1 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: 21px !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-2 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: 19px !important; font-weight: 400 !important; color: #4d4d4d !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-level-3 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-4 &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-level-5 &amp;gt; .vector-toc-link {&lt;br /&gt;
  font-size: 18px !important; font-weight: 400 !important; color: #6b6b6b !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Nesting: indent each level and run a guide line down it */&lt;br /&gt;
.vector-toc .vector-toc-contents,&lt;br /&gt;
.vector-toc .vector-toc-list { list-style: none !important; }&lt;br /&gt;
.vector-toc .vector-toc-contents { margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.vector-toc .vector-toc-list-item .vector-toc-list {&lt;br /&gt;
  margin: 0 0 0 9px !important; padding-left: 10px !important;&lt;br /&gt;
  border-left: 1px solid #ece0da;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-list-item { margin: 1px 0 !important; list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
.vector-toc .vector-toc-link:hover { background: #fbf1ec; color: var(--orange) !important; }&lt;br /&gt;
&lt;br /&gt;
/* The one entry the reader is actually on */&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current,&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current .vector-toc-text {&lt;br /&gt;
  color: var(--orange) !important; font-weight: 700 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .vector-toc-link.gr-toc-current {&lt;br /&gt;
  background: #fdf1e9; box-shadow: inset 2px 0 0 var(--orange);&lt;br /&gt;
}&lt;br /&gt;
/* Its parent sections: darker text only, so the path is readable without&lt;br /&gt;
   the whole branch lighting up */&lt;br /&gt;
.vector-toc .vector-toc-list-item.gr-toc-in-path &amp;gt; .vector-toc-link {&lt;br /&gt;
  color: var(--text) !important; font-weight: 600 !important;&lt;br /&gt;
}&lt;br /&gt;
/* Vector&#039;s own active class can sit on a parent as well as the current&lt;br /&gt;
   item, which is what made whole branches highlight — neutralised here;&lt;br /&gt;
   the gr-toc- classes above are the ones that carry the state. */&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link,&lt;br /&gt;
.vector-toc .vector-toc-list-item-active &amp;gt; .vector-toc-link * {&lt;br /&gt;
  color: inherit; font-weight: inherit;&lt;br /&gt;
}&lt;br /&gt;
.vector-toc .cdx-button.vector-toc-toggle {&lt;br /&gt;
  background: none !important; border: none !important;&lt;br /&gt;
  box-shadow: none !important; color: var(--text) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 7. HEADINGS ───────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4,&lt;br /&gt;
.mw-parser-output .mw-heading h1, .mw-parser-output .mw-heading h2,&lt;br /&gt;
.mw-parser-output .mw-heading h3 {&lt;br /&gt;
  font-family: var(--font-indic);&lt;br /&gt;
  border: none !important; padding: 0 !important; background: none !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output .mw-heading { border: none !important; margin: 0; padding: 0; }&lt;br /&gt;
&lt;br /&gt;
.mw-heading, .mw-heading2, .mw-heading3 { text-align:center; }&lt;br /&gt;
.mw-parser-output h2, .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  font-size: 1.2em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--maroon) !important; line-height: 1.8;&lt;br /&gt;
  margin: 24px 0 4px !important;&lt;br /&gt;
  border-bottom: 2px dotted var(--maroon) !important;&lt;br /&gt;
  width: 75%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-parser-output h3, .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  font-size: 1em !important; font-weight: 500 !important;&lt;br /&gt;
  color: var(--orange) !important; line-height: 1.5;&lt;br /&gt;
  margin: 24px 0 8px !important;&lt;br /&gt;
  border-bottom: 1px dotted var(--orange) !important;&lt;br /&gt;
  width: 55%; font-family: var(--font-indic); text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 8. PARAGRAPHS ─────────────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output p { margin: 0 0 0.35rem !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 9. DOCUMENT TITLE ─────────────────────────────────────────── */&lt;br /&gt;
.gr-doc-title {&lt;br /&gt;
  font-size: 2em; font-weight: 500; color: var(--maroon);&lt;br /&gt;
  line-height: 1.4; margin: 0 0 16px; border: none !important;&lt;br /&gt;
  text-align:center; font-family: var(--font-indic);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 10. VERSE BLOCKS ──────────────────────────────────────────── */&lt;br /&gt;
.shloka-block { display: block; text-align: left; margin: 3px 0 0; padding-bottom: 0.5em; }&lt;br /&gt;
.shloka-line { display: block; font-style: normal; color: #333; line-height: 1.35; margin: 0; }&lt;br /&gt;
.verse-block { display: block; margin: 6px 0 0; text-align: left; }&lt;br /&gt;
.verse-block .shloka-block { text-align: left; margin: 0; }&lt;br /&gt;
.verse-block .shloka-line {&lt;br /&gt;
  display: block; font-weight: 500; line-height: 1.35; margin: 0; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
.verse-block.normal .shloka-line { color: #555; }&lt;br /&gt;
.verse-block + p { display: none !important; margin: 0 !important; }&lt;br /&gt;
.mw-parser-output .verse-block,&lt;br /&gt;
.mw-parser-output .shloka-block { margin-top: 2px !important; margin-bottom: 2px !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-verse-text, .verse-line, .verse-text {&lt;br /&gt;
  color: #b5451b; line-height: 1.5; display: block; margin: 0.1em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-verse-type-sutra .gr-verse-text,&lt;br /&gt;
.verse-block[data-type=&amp;quot;sutra&amp;quot;] .verse-line { font-style: normal; font-weight: 600; color: #b5451b; }&lt;br /&gt;
&lt;br /&gt;
/* ── 11. UVACHA ────────────────────────────────────────────────── */&lt;br /&gt;
.uvacha-block {&lt;br /&gt;
  display: block; font-weight: bold; color: #c65;&lt;br /&gt;
  margin: 0.5rem 0 0; padding: 2px 0 2px 12px;&lt;br /&gt;
  border-left: 3px solid #c65; font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
.uvacha-block + .verse-block { margin-top: 2px; }&lt;br /&gt;
.gr-uvaaca, .uvaaca {&lt;br /&gt;
  color: #b5451b; font-weight: 600; font-size: 0.92em; margin: 0.5em 0 0.2em 0; display: block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 12. BHASHYAM ──────────────────────────────────────────────── */&lt;br /&gt;
.bhashyam-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.6; color: #555;&lt;br /&gt;
  padding: 0; border: none; background: transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-bhashyam, .commentary-bhashyam {&lt;br /&gt;
  color: #222; font-style: normal; line-height: 1.75; margin: 0.3em 0 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-leading-bhashya { color: #555; }&lt;br /&gt;
&lt;br /&gt;
/* ── 13. PADYA ─────────────────────────────────────────────────── */&lt;br /&gt;
.padya-block {&lt;br /&gt;
  display: block; font-size: 1em; line-height: 1.75; color: #c65 !important;&lt;br /&gt;
  font-weight: 500; margin: 0.4em 0 0.6em 60px;&lt;br /&gt;
}&lt;br /&gt;
.gr-padapatha { font-size: 0.9em; color: #555; margin: 0.2em 0 0.3em 0.8em; }&lt;br /&gt;
&lt;br /&gt;
/* ── 14. TEEKA ─────────────────────────────────────────────────── */&lt;br /&gt;
.teeka-block {&lt;br /&gt;
  padding: 6px 10px 8px; margin: 0.3em 0; line-height: 1.6; color: #555; background: #fcf5f3;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-title {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;;, sans-serif !important;&lt;br /&gt;
  font-size: 0.75em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  margin-bottom: 4px; padding-bottom: 3px;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-block {&lt;br /&gt;
  margin: 0.4em 0; padding: 0.3em 0.6em; font-family: &#039;AdishilaDev&#039;;&lt;br /&gt;
}&lt;br /&gt;
.teeka-body .shloka-line { font-size: 0.97em; font-family: &#039;AdishilaDev&#039;; }&lt;br /&gt;
&lt;br /&gt;
/* ── 15. PRATEEKA / MOOLA ──────────────────────────────────────── */&lt;br /&gt;
.gr-prateeka { font-weight: bold; font-style: normal; color: #333; font-family: &#039;AdishilaBold&#039; }&lt;br /&gt;
.gr-mulaprateeka-block {&lt;br /&gt;
  font-family: &#039;AdishilaItalic&#039;;&lt;br /&gt;
  text-align: center; color: #333; margin: 0.7em 0 0.3em; line-height: 1.5;&lt;br /&gt;
}&lt;br /&gt;
.gr-moola { font-weight: bold; color: #c65; }&lt;br /&gt;
&lt;br /&gt;
/* ── 16. ADHYAYA ───────────────────────────────────────────────── */&lt;br /&gt;
.adhyaya-block { margin: 0 0 4px; }&lt;br /&gt;
.adhyaya-trans { font-size: 0.9em; color: #666; margin: 2px 0 0; }&lt;br /&gt;
.adhyaya-intro { margin-top: 8px; line-height: 1.75; color: #555; }&lt;br /&gt;
.introduction p { color: var(--dark-gray); font-weight: 400; margin: 0 !important; padding: 0 !important; }&lt;br /&gt;
.introduction { padding-bottom: 0.5em; text-align: justify; }&lt;br /&gt;
&lt;br /&gt;
/* ── 17. NYAYA / PRASNA ────────────────────────────────────────── */&lt;br /&gt;
.nyaya-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.nyaya-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.nyaya-body { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-block { margin: 32px 0; padding-top: 12px; border-top: 1px solid #eee; }&lt;br /&gt;
.prasna-title {&lt;br /&gt;
  font-size: 1em; font-weight: 500; color: var(--orange);&lt;br /&gt;
  margin-bottom: 0.4rem; border-bottom: 1px dotted var(--orange); width: 50%;&lt;br /&gt;
}&lt;br /&gt;
.prasna-question, .prasna-answer { color: #555; line-height: 1.75; margin-top: 0.5rem; }&lt;br /&gt;
.prasna-answer { margin-left: 60px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 18. VYAKHYA ELEMENTS ──────────────────────────────────────── */&lt;br /&gt;
.gr-vyakhya-head, .gr-section-head {&lt;br /&gt;
  font-size: 0.9em; font-weight: 700; color: var(--orange);&lt;br /&gt;
  margin: 0.35em 0 0.15em; font-family: inherit; border: none; padding: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-avataranika {&lt;br /&gt;
  color: #444; margin: 0.2em 0 0.5em 0.5em;&lt;br /&gt;
  padding-left: 0.6em; border-left: 2px solid #c9a96e; line-height: 1.6;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-pada {&lt;br /&gt;
  font-size: 0.88em; color: #555; background: #fafafa;&lt;br /&gt;
  border: 1px solid #e8e0d0; border-radius: 4px; padding: 0.5em 0.8em;&lt;br /&gt;
  margin: 0.5em 0; line-height: 1.5; white-space: pre-wrap;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-gadya {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
.gr-vyakhya-para { margin: 0 0 0.5em 0; line-height: 1.65; color: #444; }&lt;br /&gt;
.gr-embedded-bhashya { margin: 0.2em 0 0.2em 1em; line-height: 1.6; }&lt;br /&gt;
.gr-gadya {&lt;br /&gt;
 font-family: &#039;Adishila&#039;;font-weight: bold; display: block !important; margin-left: 0.2em;}&lt;br /&gt;
.gr-author-note {&lt;br /&gt;
  font-style: italic; color: var(--maroon); text-align: center; font-size: 0.9em;&lt;br /&gt;
  margin: 0.8em 0 0.4em 0; border-top: 1px solid #ddd; padding-top: 0.4em; font-family: &#039;AdishilaDev&#039;&lt;br /&gt;
}&lt;br /&gt;
.gr-devatachandas {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-vargikarana   {     &lt;br /&gt;
    font-size: 1em;&lt;br /&gt;
    color: #f77628;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    margin-top: 10px;&lt;br /&gt;
    text-align: center; }&lt;br /&gt;
.gr-veda {&lt;br /&gt;
 font-family: &amp;quot;AdishilaVedic&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 20. BHASHYA COLLECTION ────────────────────────────────────── */&lt;br /&gt;
.mw-parser-output .bhashya-collection { margin: 0; padding: 0; }&lt;br /&gt;
.mw-parser-output .bhashya + p:empty,&lt;br /&gt;
.mw-parser-output .verse + p:empty,&lt;br /&gt;
.mw-parser-output .bhashya-collection + p:empty,&lt;br /&gt;
.mw-parser-output .pramana + p:empty,&lt;br /&gt;
.mw-parser-output .author-note + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 21. VERSE ACTIONS ─────────────────────────────────────────── */&lt;br /&gt;
.verse-actions {&lt;br /&gt;
  display: flex; flex-direction: row; align-items: center;&lt;br /&gt;
  gap: 6px; margin-top: 4px; justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn {&lt;br /&gt;
  display: inline-flex; align-items: center; justify-content: center;&lt;br /&gt;
  padding: 4px; border-radius: 6px; cursor: pointer; opacity: 0.75; transition: all 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.verse-action-btn:hover { opacity: 1; background-color: rgba(0,0,0,0.05); }&lt;br /&gt;
.icon { display: inline-block; width: 18px; height: 18px; background-repeat: no-repeat; background-position: center; background-size: contain; }&lt;br /&gt;
.icon-copy       { background-image: url(&#039;/images/copy.svg&#039;); }&lt;br /&gt;
.icon-commentary { background-image: url(&#039;/images/commentary.svg&#039;); }&lt;br /&gt;
.copy-tooltip {&lt;br /&gt;
  position: absolute; bottom: calc(100% + 8px); left: 50%;&lt;br /&gt;
  transform: translateX(-50%) translateY(4px);&lt;br /&gt;
  white-space: nowrap; pointer-events: none; z-index: 9999;&lt;br /&gt;
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;&lt;br /&gt;
  font-size: 0.75rem; font-weight: 500; background: rgba(30,30,30,0.82); color: #fff;&lt;br /&gt;
  opacity: 0; transition: opacity 0.2s, transform 0.2s;&lt;br /&gt;
}&lt;br /&gt;
.copy-tooltip.copy-tooltip-visible { opacity: 1; transform: translateX(-50%) translateY(0); }&lt;br /&gt;
&lt;br /&gt;
/* ── 22. CODE / PRE ────────────────────────────────────────────── */&lt;br /&gt;
pre, code, .mw-code, tt, kbd {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif ;&lt;br /&gt;
  background-color: transparent !important; border: none !important; padding: 0.3em 0 !important;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible, .mw-collapsible-content, blockquote, .quotebox {&lt;br /&gt;
  background-color: transparent !important; border: none !important;&lt;br /&gt;
  font-style: normal !important; padding: 0 !important; margin-left: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 23. INLINE TOC BOX (wikitext __TOC__) ─────────────────────── */&lt;br /&gt;
.toc {&lt;br /&gt;
  display: block !important; background: #fffdf5; border: 1px solid #e8c88a;&lt;br /&gt;
  border-radius: 8px; padding: 12px 16px; margin: 0 0 24px 0;&lt;br /&gt;
  width: fit-content; min-width: 220px; max-width: 420px;&lt;br /&gt;
  font-size: 0.9em; line-height: 2;&lt;br /&gt;
}&lt;br /&gt;
.toc h2, .toc .toctitle {&lt;br /&gt;
  font-size: 0.85em !important; font-weight: 600 !important; color: var(--text) !important;&lt;br /&gt;
  border: none !important; margin: 0 0 6px !important; text-transform: uppercase; letter-spacing: 0.05em;&lt;br /&gt;
}&lt;br /&gt;
.toc ul { margin: 0 !important; padding-left: 1.2em !important; list-style: none !important; }&lt;br /&gt;
.toc li { margin: 0; padding: 0; }&lt;br /&gt;
.toc a { color: var(--text) !important; text-decoration: none !important; }&lt;br /&gt;
.toc a:hover { color: var(--light-orange) !important; }&lt;br /&gt;
.toc .toclevel-1 &amp;gt; a { font-weight: 600; }&lt;br /&gt;
.tocnumber { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* ── 24. DOC TEEKAS + TOC NAV ──────────────────────────────────── */&lt;br /&gt;
.gr-doc-teekas { margin-bottom: 1em; }&lt;br /&gt;
.gr-doc-teekas + p:empty { display: none !important; margin: 0 !important; height: 0 !important; }&lt;br /&gt;
/* मूलम् / उल्लेख — real buttons, not grey chips: they are how a reader&lt;br /&gt;
   moves between the text and its commentaries, so they read as actions.&lt;br /&gt;
   Used by the desktop TOC and the mobile document nav alike. */&lt;br /&gt;
.toc-main-links {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; align-items: center;&lt;br /&gt;
  margin: 6px 0 18px; padding: 0 0 16px; border-bottom: 1px solid #f1e3dc;&lt;br /&gt;
}&lt;br /&gt;
/* Sitting under the document title in the page, not in the panel */&lt;br /&gt;
#gr-toc-doc-nav.toc-main-links { margin: 10px auto 22px; max-width: 640px; }&lt;br /&gt;
.toc-main-link-item {&lt;br /&gt;
  flex: 0 1 auto; min-width: 92px; text-align: center;&lt;br /&gt;
  font-family: var(--font-indic); font-size: 18px; font-weight: 600; line-height: 1.4;&lt;br /&gt;
  color: #fff !important; background: #b5451b; border: 0; border-radius: 999px;&lt;br /&gt;
  padding: 9px 18px; text-decoration: none !important;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.22);&lt;br /&gt;
  transition: background 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.toc-main-link-item:visited { color: #fff !important; }&lt;br /&gt;
.toc-main-link-item:hover,&lt;br /&gt;
.toc-main-link-item:focus-visible { background: #97370f !important; color: #fff !important; box-shadow: 0 2px 6px rgba(60, 20, 10, 0.26); }&lt;br /&gt;
/* Phone: the same buttons as a pill row under the document title, shaped&lt;br /&gt;
   like the reader toolbar&#039;s controls. White on the brand colour, stated&lt;br /&gt;
   with !important because the skin colours links on its own. */&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav {&lt;br /&gt;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;&lt;br /&gt;
  margin: 4px 0 14px; padding: 0 12px 14px; border-bottom: 1px solid #f0e0d6;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-mob-doc-nav .toc-main-link-item { font-size: 17px; padding: 11px 26px; min-width: 116px; }&lt;br /&gt;
.skin-minerva .toc-main-link-item,&lt;br /&gt;
.skin-minerva .toc-main-link-item:visited,&lt;br /&gt;
.skin-minerva .toc-main-link-item:active { color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 25. MAIN PAGE ─────────────────────────────────────────────── */&lt;br /&gt;
body.page-Main_Page #mw-panel,&lt;br /&gt;
body.page-Main_Page .vector-sidebar-container { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
.gr-home { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-home-page-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  text-align: center; font-size: 1.8em; font-weight: 500; color: #1a2e40; margin: 24px 0 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Section label: centred, with a rule running out on each side */&lt;br /&gt;
.gr-home-group-label {&lt;br /&gt;
  display: flex; align-items: center; gap: 16px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1.1em; font-weight: 600; color: #1a2e40;&lt;br /&gt;
  margin: 40px 0 18px; padding: 0; border: 0;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before,&lt;br /&gt;
.gr-home-group-label::after {&lt;br /&gt;
  content: &amp;quot;&amp;quot;; flex: 1; height: 1px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-group-label::before { background: linear-gradient(90deg, transparent, #e2c3b9); }&lt;br /&gt;
.gr-home-group-label::after  { background: linear-gradient(90deg, #e2c3b9, transparent); }&lt;br /&gt;
#gr-view-grantha &amp;gt; .gr-home-group-label:first-child { margin-top: 8px; }&lt;br /&gt;
&lt;br /&gt;
/* Cards sit on a grid, so every row lines up */&lt;br /&gt;
.gr-home-grid {&lt;br /&gt;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));&lt;br /&gt;
  gap: 20px; align-items: start; margin-bottom: 12px;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
  padding: 0; min-width: 0; overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-title {&lt;br /&gt;
  display: flex; align-items: center; justify-content: center; gap: 10px;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif !important;&lt;br /&gt;
  font-size: 1em; font-weight: 600; color: #1a2e40; text-align: center; line-height: 1.4;&lt;br /&gt;
  margin: 0; padding: 12px 16px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card-count {&lt;br /&gt;
  flex: none; padding: 0 8px; border-radius: 999px; background: #fff; border: 1px solid #ecd3ca;&lt;br /&gt;
  color: #c0392b; font: 600 11px/1.7 system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Numbered rows: number in a soft circle, a hairline between rows */&lt;br /&gt;
.gr-home-card ol { margin: 0; padding: 6px 0 8px; list-style: none; counter-reset: gr-item; }&lt;br /&gt;
/* Each row is a two-column grid: number | title. The number sits in the&lt;br /&gt;
   title&#039;s own row and is centred on it, so it lines up whatever the font. */&lt;br /&gt;
.gr-home-card li {&lt;br /&gt;
  counter-increment: gr-item; list-style: none;&lt;br /&gt;
  display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; align-items: center;&lt;br /&gt;
  margin: 0; padding: 8px 16px; border-top: 1px solid #f6efec;&lt;br /&gt;
  font-size: 0.95em; line-height: 1.6; color: #333; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li:first-child { border-top: 0; }&lt;br /&gt;
.gr-home-card li::before {&lt;br /&gt;
  content: counter(gr-item);&lt;br /&gt;
  grid-column: 1; grid-row: 1; justify-self: center;&lt;br /&gt;
  min-width: 22px; height: 22px; padding: 0 4px; box-sizing: border-box; border-radius: 11px;&lt;br /&gt;
  background: #fbece8; color: #c0392b; text-align: center; font: 600 11px/22px system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-home-card li &amp;gt; * { grid-column: 2; }&lt;br /&gt;
.gr-home-card li &amp;gt; a:first-child, .gr-home-card li &amp;gt; a:first-of-type { grid-row: 1; }&lt;br /&gt;
.gr-home-card li:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-home-card li a { color: #c0392b; text-decoration: none; }&lt;br /&gt;
.gr-home-card li a:hover { text-decoration: underline; text-underline-offset: 3px; }&lt;br /&gt;
.gr-home-card li a.new { color: #c08080; }&lt;br /&gt;
.gr-home-note { display: block; font-size: 0.82em; color: #888; line-height: 1.4; }&lt;br /&gt;
&lt;br /&gt;
/* A card with a long list (Madhvacharya&#039;s 38 works) spans two columns */&lt;br /&gt;
@media (min-width: 900px) {&lt;br /&gt;
  .gr-home-card.gr-home-card-wide { grid-column: span 2; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide ol { column-count: 2; column-gap: 0; column-rule: 1px solid #f3e9e5; }&lt;br /&gt;
  .gr-home-card.gr-home-card-wide li { break-inside: avoid; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 26. TOGGLE BUTTONS ────────────────────────────────────────── */&lt;br /&gt;
/* One segmented control instead of two separate buttons */&lt;br /&gt;
.gr-home-toggle {&lt;br /&gt;
  display: flex; gap: 4px; width: max-content; max-width: 100%; box-sizing: border-box;&lt;br /&gt;
  margin: 4px auto 28px; padding: 4px;&lt;br /&gt;
  background: #f7ece8; border: 1px solid #efd9d1; border-radius: 999px;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  font-size: 0.88em; padding: 7px 24px; border: 0; border-radius: 999px; text-align: center;&lt;br /&gt;
  background: transparent; color: #a0402f; cursor: pointer; user-select: none;&lt;br /&gt;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-toggle-btn:hover { background: rgba(255, 255, 255, 0.7); color: #c0392b; }&lt;br /&gt;
.gr-toggle-btn.gr-toggle-active { background: #c0392b; color: #fff; box-shadow: 0 1px 4px rgba(192, 57, 43, 0.35); }&lt;br /&gt;
.gr-toggle-btn:focus-visible { outline: 2px solid #c0392b; outline-offset: 2px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 27. SCRIPT BAR ────────────────────────────────────────────── */&lt;br /&gt;
.gr-script-bar { margin: 0 0 20px; font-size: 0.8em; color: #888; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }&lt;br /&gt;
.gr-script-label { font-family: system-ui, sans-serif; color: #aaa; }&lt;br /&gt;
.gr-script-btn { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: #c0392b !important; text-decoration: underline dotted !important; cursor: pointer; }&lt;br /&gt;
.gr-script-btn.active { color: #888 !important; text-decoration: none !important; cursor: default; }&lt;br /&gt;
&lt;br /&gt;
/* ── 28. MODALS ────────────────────────────────────────────────── */&lt;br /&gt;
.grantha-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 10000; }&lt;br /&gt;
.grantha-modal-box { background: white; padding: 20px; border-radius: 10px; width: 300px; }&lt;br /&gt;
.gm-title { font-weight: bold; margin-bottom: 10px; }&lt;br /&gt;
.gm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }&lt;br /&gt;
&lt;br /&gt;
/* ── 29. LOGIN PAGE ────────────────────────────────────────────── */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search { display: none !important; }&lt;br /&gt;
body.page-Special_UserLogin #content { max-width: 420px; margin: 40px auto !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 30. SPECIAL PAGES ─────────────────────────────────────────── */&lt;br /&gt;
body.ns-special #footer, body.ns-special .mw-footer,&lt;br /&gt;
body.ns-special .vector-footer { display: none !important; }&lt;br /&gt;
body.ns-special .vector-page-tools { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 31. MINERVA (mobile) ──────────────────────────────────────── */&lt;br /&gt;
.skin-minerva .mw-parser-output {&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  line-height: 1.8 !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h2,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading2 h2 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mw-parser-output h3,&lt;br /&gt;
.skin-minerva .mw-parser-output .mw-heading3 h3 {&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
/* Home grid single column on Minerva */&lt;br /&gt;
.skin-minerva .gr-home-grid { grid-template-columns: 1fr; gap: 14px; }&lt;br /&gt;
.skin-minerva .gr-home-toggle { width: 100%; margin: 4px 0 20px; }&lt;br /&gt;
.skin-minerva .gr-toggle-btn { flex: 1; padding: 8px 10px; }&lt;br /&gt;
.skin-minerva .gr-home-group-label { margin: 28px 0 14px; }&lt;br /&gt;
/* Hide things that should be hidden on mobile too */&lt;br /&gt;
.skin-minerva .catlinks,&lt;br /&gt;
.skin-minerva #catlinks { display: none !important; }&lt;br /&gt;
.skin-minerva .mw-editsection { display: none !important; }&lt;br /&gt;
/* Minerva already has its own TOC — hide our inline .toc */&lt;br /&gt;
.skin-minerva .toc { display: none !important; }&lt;br /&gt;
/* ReaderToolbar on Minerva */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  height: auto !important;&lt;br /&gt;
  min-height: 52px !important;&lt;br /&gt;
  overflow-x: auto !important;&lt;br /&gt;
  overflow-y: hidden !important;&lt;br /&gt;
  scrollbar-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva #gr-static-bar::-webkit-scrollbar { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-btn {&lt;br /&gt;
  height: 44px !important;&lt;br /&gt;
  min-width: 44px !important;&lt;br /&gt;
  font-size: 14px !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .gr-btn-staging { display: none !important; }&lt;br /&gt;
.skin-minerva .gr-controls .gr-sep { display: none !important; }&lt;br /&gt;
/* Minerva already shows page title — don&#039;t double-hide */&lt;br /&gt;
.skin-minerva h1.firstHeading { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── 32. PRINT ─────────────────────────────────────────────────── */&lt;br /&gt;
@media print {&lt;br /&gt;
  #gr-static-bar, .gr-controls, .vector-header-container { display: none !important; }&lt;br /&gt;
  .mw-parser-output { font-size: 11pt !important; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 33. TYPOGRAPHY CLASSES ────────────────────────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 1.09em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.4;&lt;br /&gt;
  margin: 0.45em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sub-heading — center, orange, AdishilaDevGuru */&lt;br /&gt;
.gr-sub-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.73em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #e65100;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Chapter Heading — center, maroon, AdishilaDevGuru */&lt;br /&gt;
.gr-chapter-heading {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: 500;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaDevGuru&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  margin: 0.5em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Shloka — left, bold, maroon, AdishilaVed, 5mm indent */&lt;br /&gt;
.gr-mv-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  padding-left: 0.86em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Veda Gadya — justify, bold, maroon, AdishilaVed */&lt;br /&gt;
.gr-mv-gadya {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: justify;&lt;br /&gt;
  font-size: 0.82em;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: #be100e;&lt;br /&gt;
  font-family: &#039;AdishilaVed&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  text-indent: 1.18em;&lt;br /&gt;
  padding-left: 0.36em;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  line-height: 1.75;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* M.Shloka — left, bold, orange, AdishilaDev, 5mm indent */&lt;br /&gt;
.gr-m-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
  color: var(--maroon);&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* B.Shloka / TK.Shloka / TP.Shloka — left, black, AdishilaDev */&lt;br /&gt;
.gr-b-shloka,&lt;br /&gt;
.gr-tp-shloka {&lt;br /&gt;
  display: block;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  color: #1a1a1a;&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reference — orange, AdishilaDev */&lt;br /&gt;
.gr-reference {&lt;br /&gt;
  font-family: &#039;AdishilaDev&#039;, &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; color: var(--maroon);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Custom tag span — subtle dashed underline only */&lt;br /&gt;
.gr-custom {&lt;br /&gt;
  border-bottom: 1px dashed #b5451b;&lt;br /&gt;
  padding-bottom: 1px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.gr-crosslink {&lt;br /&gt;
	color: #1a73e8;&lt;br /&gt;
	text-decoration: underline;&lt;br /&gt;
	cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
.gr-crosslink:hover {&lt;br /&gt;
	color: #0b57cf;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 34. MINERVA HEADER — orange theme + sticky ────────────────── */&lt;br /&gt;
&lt;br /&gt;
/* Kill space above header */&lt;br /&gt;
.skin-minerva body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
.skin-minerva #mw-mf-viewport,&lt;br /&gt;
.skin-minerva #mw-mf-page-center { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* Header — orange, sticky */&lt;br /&gt;
.skin-minerva header.header-container {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  position: fixed !important;&lt;br /&gt;
  top: 0 !important;&lt;br /&gt;
  z-index: 200 !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  color: #ffffff;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header {&lt;br /&gt;
  background: #b5451b !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
  min-height: 54px !important;&lt;br /&gt;
  padding: 0 12px !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.minerva-icon {&lt;br /&gt;
 &lt;br /&gt;
 filter: brightness(0) saturate(100%) invert(100%) sepia(4%) saturate(1%) hue-rotate(296deg) brightness(104%) contrast(100%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Site name text */&lt;br /&gt;
.skin-minerva .branding-box { background: transparent !important; }&lt;br /&gt;
.skin-minerva .branding-box a {&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
  display: flex !important;&lt;br /&gt;
  align-items: center !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .branding-box a span {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 18px !important;&lt;br /&gt;
  font-weight: 700 !important;&lt;br /&gt;
  font-family: system-ui, -apple-system, sans-serif !important;&lt;br /&gt;
  white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hamburger + search icons — white */&lt;br /&gt;
.skin-minerva .minerva-header label,&lt;br /&gt;
.skin-minerva .minerva-header .search-toggle {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header svg path,&lt;br /&gt;
.skin-minerva .minerva-header svg rect,&lt;br /&gt;
.skin-minerva .minerva-header svg circle {&lt;br /&gt;
  fill: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .minerva-header .mw-ui-icon:before {&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  filter: brightness(10) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Reader toolbar below Minerva header */&lt;br /&gt;
.skin-minerva #gr-static-bar {&lt;br /&gt;
  position: sticky !important;&lt;br /&gt;
  top: 54px !important;&lt;br /&gt;
  z-index: 100 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ── 35. MINERVA — auto-expand sections ────────────────────────── */&lt;br /&gt;
.skin-minerva .collapsible-block,&lt;br /&gt;
.skin-minerva .collapsible-heading + .collapsible-block {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .mf-section-0,&lt;br /&gt;
.skin-minerva .mf-section-1,&lt;br /&gt;
.skin-minerva .mf-section-2,&lt;br /&gt;
.skin-minerva .mf-section-3,&lt;br /&gt;
.skin-minerva .mf-section-4,&lt;br /&gt;
.skin-minerva .mf-section-5,&lt;br /&gt;
.skin-minerva .mf-section-6,&lt;br /&gt;
.skin-minerva .mf-section-7,&lt;br /&gt;
.skin-minerva .mf-section-8,&lt;br /&gt;
.skin-minerva .mf-section-9 {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading {&lt;br /&gt;
  pointer-events: none !important;&lt;br /&gt;
}&lt;br /&gt;
.skin-minerva .section-heading .indicator {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* ── Hide MediaWiki default header user/account controls (desktop) ── */&lt;br /&gt;
#pt-userpage-2,&lt;br /&gt;
#p-vector-user-menu-userpage,&lt;br /&gt;
/* Hide the right-side person-icon dropdown */&lt;br /&gt;
#vector-user-links-dropdown,&lt;br /&gt;
.vector-user-links-dropdown,&lt;br /&gt;
#p-personal-more,&lt;br /&gt;
.vector-user-menu,&lt;br /&gt;
.mw-portlet-vector-user-menu-overflow,&lt;br /&gt;
#pt-watchlist-2, #pt-watchlist {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide original userpage text link */&lt;br /&gt;
#p-vector-user-menu-userpage { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Hide the right-side person-icon user menu dropdown */&lt;br /&gt;
#vector-user-links-dropdown { display: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* ── Custom username dropdown ── */&lt;br /&gt;
#gr-user-dropdown {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    font-family: system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 6px;&lt;br /&gt;
    background: rgba(255,255,255,0.12);&lt;br /&gt;
    border: none;&lt;br /&gt;
    color: #fff;&lt;br /&gt;
    font-size: 0.9em;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    padding: 6px 12px;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-trigger:hover { background: rgba(255,255,255,0.2); }&lt;br /&gt;
#gr-user-trigger .gr-caret { font-size: 0.7em; opacity: 0.8; }&lt;br /&gt;
&lt;br /&gt;
#gr-user-menu {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 6px);&lt;br /&gt;
    right: 0;&lt;br /&gt;
    min-width: 180px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #e0d8d2;&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 6px 24px rgba(0,0,0,0.14);&lt;br /&gt;
    padding: 6px;&lt;br /&gt;
    z-index: 1000;&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu.gr-open { display: block; }&lt;br /&gt;
#gr-user-menu a {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    padding: 10px 12px;&lt;br /&gt;
    color: #2c1810;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
    font-size: 0.92em;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
#gr-user-menu a:hover { background: #f7f1ed; }&lt;br /&gt;
.vector-user-links a {&lt;br /&gt;
    color: #1C1A17 !important;&lt;br /&gt;
}&lt;br /&gt;
.vector-user-links-main  {&lt;br /&gt;
 display: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
/* Search Results Bar styles */&lt;br /&gt;
.gr-search-hl-mobile {&lt;br /&gt;
    position: fixed;&lt;br /&gt;
    top: 105px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    z-index: 10200;&lt;br /&gt;
    background: rgb(181, 69, 27);&lt;br /&gt;
    color: rgb(255, 255, 255);   display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: space-around;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, sans-serif;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    box-shadow: 0 3px 14px rgba(0, 0, 0, 0.08) !important;&lt;br /&gt;
    padding: 5px 4px !important;&lt;br /&gt;
    gap: 20px !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-search-hl-mobile .gr-search-bar-btn { &lt;br /&gt;
  height: 48px;&lt;br /&gt;
  width: 48px;&lt;br /&gt;
  border: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  position: relative;&lt;br /&gt;
  transition: background 0.15s;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  font-size: 12px;&lt;br /&gt;
  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;;&lt;br /&gt;
  gap: 8px;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Notes FAB, doc nav FAB and Reader toolbar on Updates/Help/About pages */&lt;br /&gt;
body.page-My_wiki_Updates #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Updates #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Updates #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_Help #gra-toggle,&lt;br /&gt;
body.page-My_wiki_Help #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_Help #gr-static-bar,&lt;br /&gt;
body.page-My_wiki_About #gra-toggle,&lt;br /&gt;
body.page-My_wiki_About #se-docnav-btn,&lt;br /&gt;
body.page-My_wiki_About #gr-static-bar { display: none; }&lt;br /&gt;
&lt;br /&gt;
/* Welcome Page */&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.page-Welcome #mw-navigation,&lt;br /&gt;
body.page-Welcome #mw-panel,&lt;br /&gt;
body.page-Welcome #footer,&lt;br /&gt;
body.page-Welcome .mw-footer,&lt;br /&gt;
body.page-Welcome #siteSub,&lt;br /&gt;
body.page-Welcome #contentSub,&lt;br /&gt;
body.page-Welcome #firstHeading,&lt;br /&gt;
body.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.page-Welcome .mw-indicators,&lt;br /&gt;
body.page-Welcome .vector-header,&lt;br /&gt;
body.page-Welcome .vector-page-toolbar,&lt;br /&gt;
body.page-Welcome .vector-sticky-header,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome #gr-static-bar, &lt;br /&gt;
body.page-Welcome #se-docnav-btn, &lt;br /&gt;
body.page-Welcome #gra-toggle,&lt;br /&gt;
body.page-Welcome #se-scrolltop {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Welcome .mw-page-container&lt;br /&gt;
&lt;br /&gt;
/* Put the banner directly on the body of the Welcome page */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make the content area transparent so the body banner shows through */&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  min-height: 100vh;&lt;br /&gt;
}&lt;br /&gt;
/* Kill the white everywhere it&#039;s painted */&lt;br /&gt;
body.page-Welcome,&lt;br /&gt;
body.page-Welcome .mw-page-container,&lt;br /&gt;
body.page-Welcome .mw-page-container-inner,&lt;br /&gt;
body.page-Welcome .mw-content-container,&lt;br /&gt;
body.page-Welcome #content,&lt;br /&gt;
body.page-Welcome .mw-body,&lt;br /&gt;
body.page-Welcome #mw-content-text,&lt;br /&gt;
body.page-Welcome .mw-parser-output,&lt;br /&gt;
body.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body and force it full height */&lt;br /&gt;
body.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-attachment: fixed !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* ---- Minerva (mobile) splash for Welcome ---- */&lt;br /&gt;
&lt;br /&gt;
/* Hide all chrome on Welcome */&lt;br /&gt;
body.skin-minerva.page-Welcome .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-header,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center &amp;gt; .header-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .banner-container,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #page-actions,&lt;br /&gt;
body.skin-minerva.page-Welcome .page-actions-menu,&lt;br /&gt;
body.skin-minerva.page-Welcome .last-modified-bar,&lt;br /&gt;
body.skin-minerva.page-Welcome .post-content,&lt;br /&gt;
body.skin-minerva.page-Welcome #footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .minerva-footer,&lt;br /&gt;
body.skin-minerva.page-Welcome .navigation-drawer,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-page-title-main,&lt;br /&gt;
body.skin-minerva.page-Welcome #section_0,&lt;br /&gt;
body.skin-minerva.page-Welcome .pre-content .mw-ui-icon,&lt;br /&gt;
body.skin-minerva.page-Welcome h1.mw-first-heading {&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Kill white on all Minerva container surfaces */&lt;br /&gt;
body.skin-minerva.page-Welcome,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-viewport,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-mf-page-center,&lt;br /&gt;
body.skin-minerva.page-Welcome #content,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-body,&lt;br /&gt;
body.skin-minerva.page-Welcome #mw-content-text,&lt;br /&gt;
body.skin-minerva.page-Welcome .mw-parser-output,&lt;br /&gt;
body.skin-minerva.page-Welcome #bodyContent {&lt;br /&gt;
  background: transparent !important;&lt;br /&gt;
  border: none !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Put the banner on the body, full height */&lt;br /&gt;
body.skin-minerva.page-Welcome {&lt;br /&gt;
  background-image: url(&#039;/images/banner.jpeg&#039;) !important;&lt;br /&gt;
  background-size: contain !important;&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
  background-position: center center !important;&lt;br /&gt;
  background-color: #3a0d0d !important;&lt;br /&gt;
  min-height: 100vh !important;&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
/* from LocalSettings line 393 */&lt;br /&gt;
/* ===== HAMBURGER / SIDEBAR ===== */&lt;br /&gt;
#mw-sidebar-button,&lt;br /&gt;
.vector-main-menu,&lt;br /&gt;
.vector-main-menu-container,&lt;br /&gt;
.vector-main-menu-landmark,&lt;br /&gt;
#vector-main-menu-dropdown {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== FOOTER ===== */&lt;br /&gt;
#footer,&lt;br /&gt;
.mw-footer,&lt;br /&gt;
footer {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== LOGIN / SIGNUP PAGE CLEANUP ===== */&lt;br /&gt;
body.page-Special_UserLogin #mw-sidebar-button,&lt;br /&gt;
body.page-Special_UserLogin .vector-main-menu,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-toolbar,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-tools,&lt;br /&gt;
body.page-Special_CreateAccount #mw-sidebar-button,&lt;br /&gt;
body.page-Special_CreateAccount .vector-main-menu,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-tools,&lt;br /&gt;
body.page-Special_PasswordReset #mw-sidebar-button,&lt;br /&gt;
body.page-Special_PasswordReset .vector-main-menu,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-tools {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 436 */&lt;br /&gt;
/* Kill every possible appearance container */&lt;br /&gt;
#vector-appearance,&lt;br /&gt;
#vector-appearance-dropdown,&lt;br /&gt;
#vector-appearance-pinned-container,&lt;br /&gt;
#vector-appearance-unpinned-container,&lt;br /&gt;
[id*=&amp;quot;appearance-dropdown&amp;quot;],&lt;br /&gt;
[id*=&amp;quot;appearance&amp;quot;][class*=&amp;quot;vector&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;Appearance&amp;quot;],&lt;br /&gt;
.vector-dropdown[title*=&amp;quot;appearance&amp;quot;] {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
    visibility:hidden !important;&lt;br /&gt;
    width:0 !important;&lt;br /&gt;
    height:0 !important;&lt;br /&gt;
    overflow:hidden !important;&lt;br /&gt;
    margin:0 !important;&lt;br /&gt;
    padding:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Remove empty slot in header */&lt;br /&gt;
.vector-page-tools,&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-header-end {&lt;br /&gt;
    gap:0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 469 */&lt;br /&gt;
/* Login page only */&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box,&lt;br /&gt;
body.page-Special_UserLogin #p-search,&lt;br /&gt;
body.page-Special_UserLogin .search-toggle,&lt;br /&gt;
body.page-Special_UserLogin .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Show site wordmark/logo if hidden */&lt;br /&gt;
body.page-Special_UserLogin .mw-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-logo,&lt;br /&gt;
body.page-Special_UserLogin .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Cleaner top bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Match Main_Page branded header bar */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Single logo: favicon only (no quill gr-header-icon) */&lt;br /&gt;
body.page-Special_UserLogin .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Hide Create Account and Login Links in header starts */&lt;br /&gt;
body.page-Special_UserLogin .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Hide Create Account and Login Links in header end */&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 530 */&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box,&lt;br /&gt;
body.page-Special_PasswordReset #p-search,&lt;br /&gt;
body.page-Special_PasswordReset .search-toggle,&lt;br /&gt;
body.page-Special_PasswordReset .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-logo,&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 584 */&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box,&lt;br /&gt;
body.page-Special_CreateAccount #p-search,&lt;br /&gt;
body.page-Special_CreateAccount .search-toggle,&lt;br /&gt;
body.page-Special_CreateAccount .vector-search-box-vue {&lt;br /&gt;
    display:none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-logo,&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-start {&lt;br /&gt;
    display:flex !important;&lt;br /&gt;
    visibility:visible !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header {&lt;br /&gt;
    justify-content:space-between !important;&lt;br /&gt;
    padding:0 16px !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-container .mw-header {&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark,&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-wordmark strong {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-container {&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-header-icon {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-logo-icon {&lt;br /&gt;
    display: inline-block !important;&lt;br /&gt;
    width: 36px !important;&lt;br /&gt;
    height: 36px !important;&lt;br /&gt;
    object-fit: contain !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-header-end {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 635 */&lt;br /&gt;
body.page-Special_UserLogin {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container,&lt;br /&gt;
body.page-Special_UserLogin .mw-page-container-inner,&lt;br /&gt;
body.page-Special_UserLogin .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-header,&lt;br /&gt;
body.page-Special_UserLogin .vector-page-titlebar,&lt;br /&gt;
body.page-Special_UserLogin #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #bodyContent,&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginprompt:empty {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Login/Signup&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 24px;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #userloginForm,&lt;br /&gt;
body.page-Special_UserLogin #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-label,&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* Codex: border on wrapper only (inner input has its own border = double line) */&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #ffffff !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
/* Fallback if inputs are not wrapped in Codex */&lt;br /&gt;
body.page-Special_UserLogin input#wpName1,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1 {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin input#wpName1:focus,&lt;br /&gt;
body.page-Special_UserLogin input#wpPassword1:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-rememberme {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password {&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    margin: 2px 0 14px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-login-forgot-password a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-field:has(#wpLoginAttempt),&lt;br /&gt;
body.page-Special_UserLogin .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt,&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin #wpLoginAttempt:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    margin-top: 12px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .mw-userlogin-help a:hover,&lt;br /&gt;
body.page-Special_UserLogin .mw-form-related-link-container a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount a:hover,&lt;br /&gt;
body.page-Special_UserLogin #mw-createaccount-cta a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .cdx-message,&lt;br /&gt;
body.page-Special_UserLogin .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
    margin-top: -3px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #F3F4F6;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #F4ECDF;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_UserLogin .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #F4ECDF;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-row { margin-bottom: 12px; }&lt;br /&gt;
.gr-phone-label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-inputs {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
    height: 48px;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-phone-country { width: 101px; border-radius: 12px; padding: 12px; gap: 8px; border: 1px solid #CED4DA;}&lt;br /&gt;
.gr-phone-national { flex: 1; border-radius: 12px; padding: 12px; border: 1px solid #CED4DA;}&lt;br /&gt;
&lt;br /&gt;
/* ── Country trigger button ── */&lt;br /&gt;
.gr-country-trigger {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    width: 101px;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    padding: 12px;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-trigger:hover { border-color: #72777d; }&lt;br /&gt;
.gr-trigger-flag { font-size: 1.3rem; line-height: 1; }&lt;br /&gt;
.gr-trigger-dial { font-size: 0.9rem; color: #54595d; }&lt;br /&gt;
.gr-trigger-caret { font-size: 0.7rem; color: #54595d; }&lt;br /&gt;
&lt;br /&gt;
/* ── Dropdown ── */&lt;br /&gt;
.gr-country-dropdown {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: calc(100% + 4px); left: 0;&lt;br /&gt;
    z-index: 9999;&lt;br /&gt;
    width: 280px;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);&lt;br /&gt;
}&lt;br /&gt;
.gr-country-search {&lt;br /&gt;
    width: 100%; box-sizing: border-box;&lt;br /&gt;
    padding: 8px 12px;&lt;br /&gt;
    border: none; border-bottom: 1px solid #eee;&lt;br /&gt;
    font-size: 0.9rem; outline: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-list { max-height: 240px; overflow-y: auto; }&lt;br /&gt;
.gr-country-option {&lt;br /&gt;
    display: flex; align-items: center; gap: 8px;&lt;br /&gt;
    padding: 8px 12px; cursor: pointer; font-size: 0.9rem;&lt;br /&gt;
}&lt;br /&gt;
.gr-country-option:hover { background: #f8f9fa; }&lt;br /&gt;
.gr-country-flag { font-size: 1.2rem; flex-shrink: 0; }&lt;br /&gt;
.gr-country-name { flex: 1; color: #202122; }&lt;br /&gt;
.gr-country-dial { color: #54595d; font-size: 0.85rem; }&lt;br /&gt;
&lt;br /&gt;
.gr-phone-national:focus {&lt;br /&gt;
    outline: none; border-color: #4A433A;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OTP verification screen */&lt;br /&gt;
#gr-otp-screen { padding: 8px 0; }&lt;br /&gt;
.gr-otp-title { font-size: 24px; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif; text-align: center; margin-bottom: 12px; color: #1C1A17}&lt;br /&gt;
.gr-otp-desc { font-size: 16px; text-align: center; color: #1C1A17; margin-bottom: 20px; line-height: 1.5; font-family: &amp;quot;Noto Sans Devanagari&amp;quot; }&lt;br /&gt;
.gr-otp-label { display: block; font-size: 16px; margin-bottom: 8px;  font-family: &amp;quot;Noto Sans Devanagari&amp;quot;; color: #1C1A17}&lt;br /&gt;
.gr-otp-boxes {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    gap: 8px;&lt;br /&gt;
    margin-bottom: 20px;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box {&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
    max-width: 52px;&lt;br /&gt;
    width: 52px;&lt;br /&gt;
    height: 52px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 20px;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    border: 1px solid #CED4DA;&lt;br /&gt;
    border-radius: 12px;&lt;br /&gt;
    outline: none;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-box:focus { border-color: #4A433A; }&lt;br /&gt;
&lt;br /&gt;
.gr-continue-btn:not(:disabled):hover { background: #B14A2E; }&lt;br /&gt;
.gr-continue-btn:disabled { opacity: 0.6; cursor: not-allowed; }&lt;br /&gt;
.gr-resend-row { text-align: center; font-size: 14px; color: #54595d; }&lt;br /&gt;
.gr-link-btn {&lt;br /&gt;
    background: none; border: none; padding: 0;&lt;br /&gt;
    color: #B14A2E; cursor: pointer; font-size: inherit;&lt;br /&gt;
    text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.gr-link-btn:hover { text-decoration: underline; }&lt;br /&gt;
&lt;br /&gt;
/* Login button on phone screen */&lt;br /&gt;
.gr-login-btn, .gr-continue-btn {&lt;br /&gt;
    width: 100%; padding: 16px 8px; border: none; gap: 8px;&lt;br /&gt;
    border-radius: 12px; background: #B14A2E;&lt;br /&gt;
    color: #fff; font-size: 16px; font-weight: 500;&lt;br /&gt;
    cursor: pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
.gr-login-btn:hover { background: #993C1D; }&lt;br /&gt;
.gr-otp-hidden-field {&lt;br /&gt;
    position: absolute !important;&lt;br /&gt;
    width: 1px !important;&lt;br /&gt;
    height: 1px !important;&lt;br /&gt;
    overflow: hidden !important;&lt;br /&gt;
    clip: rect(0,0,0,0) !important;&lt;br /&gt;
    white-space: nowrap !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-otp-error {&lt;br /&gt;
    background: #fce8e8;&lt;br /&gt;
    border: 1px solid #e57373;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    color: #b71c1c;&lt;br /&gt;
    padding: 10px 14px;&lt;br /&gt;
    margin-bottom: 12px;&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
    .gr-login-checks{display:flex;justify-content: space-between; gap:8px;margin:12px 0;}&lt;br /&gt;
.gr-check-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#1C1A17;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
.gr-check-label input{width:16px;height:16px;accent-color:#B14A2E;cursor:pointer; font-family: &amp;quot;Noto Sans Devanagari&amp;quot;}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_UserLogin .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_UserLogin #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1021 */&lt;br /&gt;
body.page-Special_PasswordReset {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container,&lt;br /&gt;
body.page-Special_PasswordReset .mw-page-container-inner,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-header,&lt;br /&gt;
body.page-Special_PasswordReset .vector-page-titlebar,&lt;br /&gt;
body.page-Special_PasswordReset #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset #bodyContent,&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper::before {&lt;br /&gt;
    content: &amp;quot;Reset password&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-header p {&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-ooui-wrapper .oo-ui-fieldLayout {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-header .oo-ui-labelElement-label {&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    font-weight: 500;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget .oo-ui-inputWidget-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget.oo-ui-widget-focus .oo-ui-inputWidget-input {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-fieldLayout-field {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .oo-ui-textInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement,&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonInputWidget {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .mw-htmlform-submit-buttons .oo-ui-buttonElement-button:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
    border-color: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .gr-passwordreset-footer a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_PasswordReset .cdx-message,&lt;br /&gt;
body.page-Special_PasswordReset .mw-message-box,&lt;br /&gt;
body.page-Special_PasswordReset .successbox,&lt;br /&gt;
body.page-Special_PasswordReset .warningbox,&lt;br /&gt;
body.page-Special_PasswordReset .errorbox {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
    body.page-Special_PasswordReset .mw-body-content &amp;gt; .mw-htmlform-ooui-wrapper {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_PasswordReset #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1197 */&lt;br /&gt;
body.page-Special_CreateAccount {&lt;br /&gt;
    background: #FAF6EE !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container,&lt;br /&gt;
body.page-Special_CreateAccount .mw-page-container-inner,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body {&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-header,&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-titlebar,&lt;br /&gt;
body.page-Special_CreateAccount #firstHeading {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #content {&lt;br /&gt;
    max-width: 440px !important;&lt;br /&gt;
    margin: 28px auto 48px !important;&lt;br /&gt;
    padding: 0 16px !important;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #bodyContent,&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content {&lt;br /&gt;
    margin: 0 !important;&lt;br /&gt;
    padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .vector-page-toolbar {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    border-radius: 16px;&lt;br /&gt;
    box-shadow: 0 10px 40px rgba(45, 35, 72, 0.1);&lt;br /&gt;
    padding: 32px 32px 28px;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container::before {&lt;br /&gt;
    content: &amp;quot;Create account&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    font-size: 1.65rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    margin-bottom: 22px;&lt;br /&gt;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupstart {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-createacct-benefits-container {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm,&lt;br /&gt;
body.page-Special_CreateAccount #userloginForm form {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field {&lt;br /&gt;
    margin-bottom: 18px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-label,&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field &amp;gt; label {&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 0.875rem;&lt;br /&gt;
    color: #1C1A17;&lt;br /&gt;
    margin-bottom: 6px;&lt;br /&gt;
    font-family: Noto Sans Devanagari UI, system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    background: #fff !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 0 !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    background: transparent !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 44px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-field__help-text,&lt;br /&gt;
body.page-Special_CreateAccount .htmlform-tip {&lt;br /&gt;
    font-size: 0.8rem;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    margin-top: 6px;&lt;br /&gt;
    line-height: 1.4;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input:focus-within {&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-text-input__input:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    border: 1px solid #e5e7eb !important;&lt;br /&gt;
    padding: 12px 14px !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    min-height: 46px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount input#wpName2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpPassword2:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpRetype:focus,&lt;br /&gt;
body.page-Special_CreateAccount input#wpEmail:focus {&lt;br /&gt;
    outline: none !important;&lt;br /&gt;
    border-color: #b5451b !important;&lt;br /&gt;
    box-shadow: rgba(177, 74, 46, 0.1) !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-field:has(#wpCreateaccount),&lt;br /&gt;
body.page-Special_CreateAccount .mw-htmlform-submit {&lt;br /&gt;
    margin-top: 8px;&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount,&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount.cdx-button {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    background: #b5451b !important;&lt;br /&gt;
    border: none !important;&lt;br /&gt;
    border-radius: 10px !important;&lt;br /&gt;
    color: #fff !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    font-size: 1rem !important;&lt;br /&gt;
    padding: 14px 20px !important;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    cursor: pointer;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #wpCreateaccount:hover {&lt;br /&gt;
    background: #A33C20 !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    margin-top: 18px;&lt;br /&gt;
    padding-top: 16px;&lt;br /&gt;
    border-top: 1px solid #f0eef5;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-text {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-login-signup-link:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container {&lt;br /&gt;
    font-size: 0.9rem;&lt;br /&gt;
    color: #6b7280;&lt;br /&gt;
    margin-top: 14px;&lt;br /&gt;
    line-height: 1.5;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a {&lt;br /&gt;
    color: #b5451b !important;&lt;br /&gt;
    font-weight: 600 !important;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount #signupend a:hover,&lt;br /&gt;
body.page-Special_CreateAccount .mw-form-related-link-container a:hover {&lt;br /&gt;
    text-decoration: underline !important;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .cdx-message,&lt;br /&gt;
body.page-Special_CreateAccount .mw-message-box {&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    margin-bottom: 16px;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login {&lt;br /&gt;
    margin: 20px 0 4px;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon {&lt;br /&gt;
    display: inline-flex;&lt;br /&gt;
    flex-shrink: 0;&lt;br /&gt;
    line-height: 0;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-icon svg {&lt;br /&gt;
    display: block;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    gap: 10px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
    padding: 12px 16px;&lt;br /&gt;
    min-height: 48px;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px solid #e5e7eb;&lt;br /&gt;
    background: #fff;&lt;br /&gt;
    color: #2d2348;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    font-size: 1rem;&lt;br /&gt;
    text-decoration: none !important;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-btn:hover {&lt;br /&gt;
    background: #f9fafb;&lt;br /&gt;
    border-color: #d1d5db;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    gap: 12px;&lt;br /&gt;
    margin: 18px 0 16px;&lt;br /&gt;
    color: #9ca3af;&lt;br /&gt;
    font-size: 0.85rem;&lt;br /&gt;
    font-family: system-ui, -apple-system, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::before,&lt;br /&gt;
body.page-Special_CreateAccount .gr-google-login-divider::after {&lt;br /&gt;
    content: &amp;quot;&amp;quot;;&lt;br /&gt;
    flex: 1;&lt;br /&gt;
    height: 1px;&lt;br /&gt;
    background: #e5e7eb;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    .page-Special_UserLogin #mw-mf-page-center,&lt;br /&gt;
.page-Special_UserLogin .mw-mf-page-center {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    align-items: center !important;&lt;br /&gt;
    justify-content: center !important;&lt;br /&gt;
    min-height: 100vh !important;&lt;br /&gt;
    padding: 16px !important;&lt;br /&gt;
    box-sizing: border-box !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.page-Special_UserLogin .mw-ui-container {&lt;br /&gt;
    width: 100% !important;&lt;br /&gt;
    max-width: 400px !important;&lt;br /&gt;
    margin: 0 auto !important;&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
@media (max-width: 480px) {&lt;br /&gt;
&lt;br /&gt;
    body.page-Special_CreateAccount .mw-body-content &amp;gt; .mw-ui-container {&lt;br /&gt;
        padding: 24px 20px 20px;&lt;br /&gt;
        border-radius: 12px;&lt;br /&gt;
    }&lt;br /&gt;
    body.page-Special_CreateAccount #content {&lt;br /&gt;
        margin: 16px auto 32px !important;&lt;br /&gt;
        padding: 0 12px !important;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1484 */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
        .vector-page-toolbar-container,&lt;br /&gt;
        .mw-page-container {&lt;br /&gt;
            border-top: 0 !important;&lt;br /&gt;
            box-shadow: none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .vector-page-toolbar {&lt;br /&gt;
            display:none !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        .mw-body {&lt;br /&gt;
            margin-top:0 !important;&lt;br /&gt;
            padding-top:0 !important;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
/* from LocalSettings line 1525 */&lt;br /&gt;
.mw-logo-wordmark,&lt;br /&gt;
.vector-header-start a.mw-wiki-title {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    flex-direction: column !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-logo-wordmark::after,&lt;br /&gt;
.vector-header-start a.mw-wiki-title::after {&lt;br /&gt;
    content: &amp;quot;A Digital Archive of Dvaita Vedanta Literature&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
    font-size: 11px;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    color: #ffffff;&lt;br /&gt;
    letter-spacing: 0.3px;&lt;br /&gt;
    margin-top: 1px;&lt;br /&gt;
    font-family: sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ===== namespace toolbar hiding (was BeforePageDisplay line 1484) =====&lt;br /&gt;
   Original: hide top toolbar on all namespaces EXCEPT Special, Template, MediaWiki.&lt;br /&gt;
   MediaWiki adds body classes ns-0, ns-10 (Template), ns-8 (MediaWiki), ns--1 (Special).&lt;br /&gt;
   So: hide globally, then re-show on the allowed namespaces. */&lt;br /&gt;
.vector-page-toolbar,&lt;br /&gt;
.vector-page-toolbar-container,&lt;br /&gt;
.mw-page-container { border-top: 0 !important; box-shadow: none !important; }&lt;br /&gt;
.vector-page-toolbar { display: none !important; }&lt;br /&gt;
.mw-body { margin-top: 0 !important; padding-top: 0 !important; }&lt;br /&gt;
&lt;br /&gt;
/* re-show toolbar on Special / Template / MediaWiki namespaces */&lt;br /&gt;
body.ns--1 .vector-page-toolbar,&lt;br /&gt;
body.ns-10 .vector-page-toolbar,&lt;br /&gt;
body.ns-8  .vector-page-toolbar { display: block !important; }&lt;br /&gt;
&lt;br /&gt;
/* Style overrides for QuickEdit */&lt;br /&gt;
.mw-body-content[contenteditable=&amp;quot;true&amp;quot;]:focus-visible {&lt;br /&gt;
 outline: 0;&lt;br /&gt;
}&lt;br /&gt;
/* ── 36. COMMENTARY INTERLEAVING (Show/hide teeka in moola) ─────── */&lt;br /&gt;
.gr-injected-teeka-group { margin: 0 0 6px 0; }&lt;br /&gt;
&lt;br /&gt;
/* Inline checkbox placed directly before an existing commentary link&lt;br /&gt;
   wherever it appears on the page (e.g. a &amp;quot;वादावली&amp;quot; list) - same maroon&lt;br /&gt;
   accent as the toolbar dropdown&#039;s own checkboxes, for visual&lt;br /&gt;
   consistency between the two places a reader can toggle a commentary.&lt;br /&gt;
&lt;br /&gt;
   CHANGED: was relying on vertical-align + a plain width/height on the&lt;br /&gt;
   bare &amp;lt;input&amp;gt; - confirmed directly (screenshot) this rendered&lt;br /&gt;
   noticeably oversized and misaligned with the surrounding text, since&lt;br /&gt;
   native checkbox rendering size/baseline varies significantly across&lt;br /&gt;
   browsers/OS (especially mobile Safari) and isn&#039;t reliably constrained&lt;br /&gt;
   by width/height alone without also resetting the native appearance.&lt;br /&gt;
   Now wrapped in an inline-flex span the checkbox sits inside, which&lt;br /&gt;
   centers it against the line&#039;s actual content height regardless of&lt;br /&gt;
   the checkbox&#039;s own native box quirks - a more robust fix than tuning&lt;br /&gt;
   vertical-align offsets by hand. */&lt;br /&gt;
.gr-teeka-cb-wrap {&lt;br /&gt;
  display: inline-flex; align-items: center; gap: 6px;&lt;br /&gt;
  vertical-align: middle;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 15px !important; height: 15px !important;&lt;br /&gt;
  min-width: 15px; min-height: 15px;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; flex-shrink: 0;&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked {&lt;br /&gt;
  background: #b5451b; border-color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-inline-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4px; top: 1px;&lt;br /&gt;
  width: 4px; height: 8px;&lt;br /&gt;
  border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* CHANGED: bullets removed from any &amp;lt;li&amp;gt; this feature attaches a&lt;br /&gt;
   checkbox to - the checkbox itself is now the visual marker, so the&lt;br /&gt;
   original bullet point is redundant clutter, not a second marker. */&lt;br /&gt;
.gr-teeka-list-item { list-style: none !important; }&lt;br /&gt;
&lt;br /&gt;
/* Toolbar &amp;quot;Commentaries&amp;quot; dropdown button - matches .gr-btn-edit /&lt;br /&gt;
   .gr-btn-settings exactly (border #f0cfc4, maroon text, cream hover). */&lt;br /&gt;
/* CHANGED: pill shape + soft shadow, matching the toolbar-wide &amp;quot;soft&lt;br /&gt;
   rounded pill&amp;quot; redesign - consistent with .gr-btn&#039;s own border-radius&lt;br /&gt;
   and box-shadow treatment in readerToolbar.js rather than this one&lt;br /&gt;
   button keeping the old sharp-cornered bordered look on its own. */&lt;br /&gt;
/* CHANGED: was styled for a text label + caret (&amp;quot;टीका ▾&amp;quot;) - now an&lt;br /&gt;
   icon-only eye.svg button, so font-size and the caret rule are no&lt;br /&gt;
   longer relevant and removed; border-radius reduced to match the&lt;br /&gt;
   toolbar-wide radius reduction (see readerToolbar.js). */&lt;br /&gt;
.gr-teeka-dropdown-btn { border: none; border-radius: 12px; box-shadow: 0 2px 6px rgba(181,69,27,0.22); transition: box-shadow .15s, transform .15s; }&lt;br /&gt;
.gr-teeka-dropdown-btn:hover { background: #fdf0eb; box-shadow: 0 4px 14px rgba(181,69,27,0.34); transform: translateY(-1px); }&lt;br /&gt;
&lt;br /&gt;
.gr-teeka-dropdown-panel {&lt;br /&gt;
  background: #fff; border: 1px solid #f0cfc4; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 4px 16px rgba(181,69,27,0.16); max-height: 60vh; overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-header {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b; padding: 10px 14px 6px; border-bottom: 1px solid #f7e4da; margin-bottom: 2px;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-list { padding: 4px 0; }&lt;br /&gt;
.gr-teeka-dropdown-item {&lt;br /&gt;
  display: flex; align-items: center; gap: 9px; padding: 8px 14px;&lt;br /&gt;
  font-size: 14px; color: #3a2a20; cursor: pointer; white-space: nowrap;&lt;br /&gt;
  transition: background .12s;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-item:hover { background: #fdf0eb; }&lt;br /&gt;
/* CHANGED: matches the same robust custom-checkbox approach as&lt;br /&gt;
   .gr-teeka-inline-checkbox above, rather than accent-color alone,&lt;br /&gt;
   which doesn&#039;t reliably constrain native checkbox SIZE across&lt;br /&gt;
   browsers even though it does control the checked-state color. */&lt;br /&gt;
.gr-teeka-dropdown-checkbox {&lt;br /&gt;
  -webkit-appearance: none; appearance: none;&lt;br /&gt;
  width: 16px !important; height: 16px !important;&lt;br /&gt;
  min-width: 16px; min-height: 16px; flex-shrink: 0;&lt;br /&gt;
  margin: 0; padding: 0; cursor: pointer;&lt;br /&gt;
  border: 1.5px solid #b5451b; border-radius: 3px;&lt;br /&gt;
  background: #fff; position: relative; box-sizing: border-box;&lt;br /&gt;
}&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked { background: #b5451b; border-color: #b5451b; }&lt;br /&gt;
.gr-teeka-dropdown-checkbox:checked::after {&lt;br /&gt;
  content: &#039;&#039;; position: absolute; left: 4.5px; top: 1.5px;&lt;br /&gt;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;&lt;br /&gt;
  transform: rotate(45deg);&lt;br /&gt;
}&lt;br /&gt;
/* Sub-commentaries (tippanis) nested under their parent teeka - shown&lt;br /&gt;
   only once the parent is checked (see readerToolbar.js), indented to&lt;br /&gt;
   read as a sub-category rather than a peer option. */&lt;br /&gt;
.gr-teeka-dropdown-subitem { padding-left: 34px !important; }&lt;br /&gt;
.gr-teeka-dropdown-sublist { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-sublist.gr-open { display: block; }&lt;br /&gt;
.gr-teeka-dropdown-item-label {&lt;br /&gt;
  overflow: hidden; text-overflow: ellipsis;&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, system-ui, sans-serif;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Toolbar control only needs to be shown as a fallback once the inline&lt;br /&gt;
   checkbox list scrolls out of view - hidden by default, toggled by&lt;br /&gt;
   readerToolbar.js listening to window.grTeekaInterleave.onVisibilityChange. */&lt;br /&gt;
.gr-teeka-dropdown-wrap { display: none; }&lt;br /&gt;
.gr-teeka-dropdown-wrap.gr-visible { display: inline-block; }&lt;br /&gt;
&lt;br /&gt;
/* ── 37. HEADER LINKS + UPDATES PAGE ───────────────────────────── */&lt;br /&gt;
/* Desktop header links (Help, About, Login, Library, Updates) */&lt;br /&gt;
.gr-header-link {&lt;br /&gt;
  display: inline-flex; align-items: center;&lt;br /&gt;
  color: rgba(255, 255, 255, 0.88) !important; font-size: 0.88em; font-family: system-ui, sans-serif; font-weight: 500;&lt;br /&gt;
  line-height: inherit; text-decoration: none !important; white-space: nowrap;&lt;br /&gt;
  padding: 4px 10px; margin: 0 4px 0 0; border: 0; border-radius: 4px; background: transparent; cursor: pointer;&lt;br /&gt;
  transition: color 0.15s, background 0.15s;&lt;br /&gt;
}&lt;br /&gt;
.gr-header-link:hover,&lt;br /&gt;
.gr-header-link:focus-visible,&lt;br /&gt;
.gr-header-link.is-current { color: #fff !important; background: rgba(255, 255, 255, 0.12); }&lt;br /&gt;
&lt;br /&gt;
/* Mobile menu panel items (Library, Updates) — same look as Home/Help */&lt;br /&gt;
.gr-mob-item {&lt;br /&gt;
  display: flex; align-items: center; width: 100%; box-sizing: border-box; margin: 0;&lt;br /&gt;
  padding: 15px 20px; border-bottom: 1px solid #f0ebe6; border-left: 3px solid transparent; background: #fff;&lt;br /&gt;
  font-size: 16px; font-family: system-ui, sans-serif; color: #2c1810 !important;&lt;br /&gt;
  text-decoration: none !important;&lt;br /&gt;
}&lt;br /&gt;
.gr-mob-item.is-current { border-left-color: #c0392b; background: #fdf8f5; color: #c0392b !important; font-weight: 600; }&lt;br /&gt;
#gr-header-user-menu a.is-current { background: #f5efe9; font-weight: 600; }&lt;br /&gt;
&lt;br /&gt;
/* Updates page (My_wiki:Updates) */&lt;br /&gt;
.gr-updates-page { max-width: 1140px; margin: 0 auto; padding: 0 0 40px; }&lt;br /&gt;
.gr-updates-intro { margin: 0 0 24px; font-family: system-ui, sans-serif; font-size: 15px; color: #666; line-height: 1.6; }&lt;br /&gt;
.gr-updates-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 28px; align-items: start; }&lt;br /&gt;
.gr-updates-layout.gr-no-bulletin { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
.gr-updates-heading,&lt;br /&gt;
.gr-bulletin-heading {&lt;br /&gt;
  font-family: system-ui, sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;&lt;br /&gt;
  color: #b5451b;&lt;br /&gt;
}&lt;br /&gt;
.gr-updates-heading { margin: 0 0 6px; }&lt;br /&gt;
&lt;br /&gt;
/* What&#039;s new: grouped list */&lt;br /&gt;
.gr-updates-feed { min-height: 120px; }&lt;br /&gt;
.gr-upd-group { margin: 22px 0 8px; font: 600 14px system-ui, sans-serif; color: #1a2e40; }&lt;br /&gt;
.gr-upd-group:first-child { margin-top: 8px; }&lt;br /&gt;
.gr-upd-list {&lt;br /&gt;
  margin: 0; padding: 0;&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-radius: 10px; overflow: hidden;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06);&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item {&lt;br /&gt;
  display: flex; align-items: baseline; gap: 16px;&lt;br /&gt;
  margin: 0; padding: 12px 18px; border-top: 1px solid #f6efec; border-left: 3px solid transparent;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-upd-item:hover { background: #fdf8f6; }&lt;br /&gt;
.gr-upd-item.gr-upd-unseen { border-left-color: #c0392b; background: #fffaf8; }&lt;br /&gt;
.gr-upd-main { flex: 1; min-width: 0; }&lt;br /&gt;
.gr-upd-title {&lt;br /&gt;
  font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1em; font-weight: 600; line-height: 1.45;&lt;br /&gt;
  color: #c0392b !important; text-decoration: none !important; overflow-wrap: anywhere;&lt;br /&gt;
}&lt;br /&gt;
.gr-upd-title:hover { text-decoration: underline !important; text-underline-offset: 3px; }&lt;br /&gt;
.gr-upd-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 3px; font-size: 13px; color: #777; }&lt;br /&gt;
.gr-upd-work { font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 14px; }&lt;br /&gt;
.gr-upd-tag { flex: none; padding: 1px 8px; border-radius: 9px; font: 600 11px/1.6 system-ui, sans-serif; }&lt;br /&gt;
.gr-upd-tag.gr-upd-new    { background: #fbece8; color: #c0392b; }&lt;br /&gt;
.gr-upd-tag.gr-upd-update { background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-upd-date { flex: none; font: 13px system-ui, sans-serif; color: #888; white-space: nowrap; }&lt;br /&gt;
.gr-upd-empty { margin: 12px 0; padding: 24px; text-align: center; font: 14px system-ui, sans-serif; color: #888;&lt;br /&gt;
  background: #fff; border: 1px dashed #e6d6cf; border-radius: 10px; }&lt;br /&gt;
&lt;br /&gt;
/* Announcements &amp;amp; events card */&lt;br /&gt;
.gr-bulletin {&lt;br /&gt;
  background: #fff; border: 1px solid #efe4df; border-top: 3px solid #c0392b; border-radius: 10px;&lt;br /&gt;
  box-shadow: 0 1px 3px rgba(60, 20, 10, 0.06); overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin.gr-bulletin-is-empty { display: none; }&lt;br /&gt;
.gr-bulletin-heading { padding: 12px 18px; background: #fcf6f3; border-bottom: 1px solid #f1e2dc; }&lt;br /&gt;
.gr-bulletin-item { padding: 14px 18px; border-top: 1px solid #f6efec; }&lt;br /&gt;
.gr-bulletin-item:first-child { border-top: 0; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-expired { display: none; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past { opacity: 0.55; }&lt;br /&gt;
.gr-bulletin-meta { display: flex; align-items: center; gap: 8px; font: 12px system-ui, sans-serif; color: #888; }&lt;br /&gt;
.gr-bulletin-type { padding: 1px 8px; border-radius: 9px; font-weight: 600; font-size: 11px; background: #eaf1fb; color: #2a5aa8; }&lt;br /&gt;
.gr-bulletin-type::before { content: &amp;quot;Announcement&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type { background: #fdf1dc; color: #9a5b00; }&lt;br /&gt;
.gr-bulletin-item[data-type=&amp;quot;event&amp;quot;] .gr-bulletin-type::before { content: &amp;quot;Event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item.gr-bulletin-past .gr-bulletin-type::before { content: &amp;quot;Past event&amp;quot;; }&lt;br /&gt;
.gr-bulletin-item-title {&lt;br /&gt;
  margin-top: 6px; font-family: &#039;Adishila&#039;, &#039;Noto Serif Devanagari&#039;, serif; font-size: 1.05em; font-weight: 600;&lt;br /&gt;
  line-height: 1.45; color: #1a2e40;&lt;br /&gt;
}&lt;br /&gt;
.gr-bulletin-item-title a { color: #c0392b; }&lt;br /&gt;
.gr-bulletin-where { display: block; margin-top: 2px; font: 13px system-ui, sans-serif; color: #666; }&lt;br /&gt;
.gr-bulletin-item-text { margin-top: 4px; font-size: 0.92em; line-height: 1.6; color: #444; }&lt;br /&gt;
.gr-bulletin-item-text p { margin: 0; }&lt;br /&gt;
.gr-bulletin-where:empty, .gr-bulletin-item-text:empty, .gr-bulletin-date:empty { display: none; }&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 900px) {&lt;br /&gt;
  .gr-updates-layout { grid-template-columns: minmax(0, 1fr); }&lt;br /&gt;
  .gr-bulletin { order: -1; }   /* announcements first on small screens */&lt;br /&gt;
}&lt;br /&gt;
/* Phones: title on its own line, tag + date underneath */&lt;br /&gt;
@media (max-width: 600px) {&lt;br /&gt;
  .gr-updates-intro { font-size: 14px; margin-bottom: 18px; }&lt;br /&gt;
  .gr-upd-item { flex-direction: column; align-items: stretch; gap: 2px; padding: 11px 14px; }&lt;br /&gt;
  .gr-upd-title { font-size: 0.9em; }&lt;br /&gt;
  .gr-upd-date { font-size: 12px; }&lt;br /&gt;
  .gr-bulletin-heading { padding: 10px 14px; }&lt;br /&gt;
  .gr-bulletin-item { padding: 12px 14px; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Minerva menu panel: our list replaces the skin&#039;s; the skin&#039;s lists are&lt;br /&gt;
   kept (hidden) because Minerva&#039;s own script looks them up. */&lt;br /&gt;
#mw-mf-page-left &amp;gt; .gr-mf-native-menu { display: none !important; }&lt;br /&gt;
.gr-mob-menu { width: 100%; margin-top: 8px; background: #fff; }&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
	<entry>
		<id>https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7515</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://dev.anandamakaranda.in/index.php?title=MediaWiki:Common.js&amp;diff=7515"/>
		<updated>2026-09-23T13:03:48Z</updated>

		<summary type="html">&lt;p&gt;Vaishnavi: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* MediaWiki:Common.js */&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_SCRIPT_KEY = &#039;grantha_reader_script&#039;;&lt;br /&gt;
    var currentScript = &#039;deva&#039;;&lt;br /&gt;
&lt;br /&gt;
    function devanagariToTamil(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;க&#039;, &#039;ख&#039;: &#039;க&#039;, &#039;ग&#039;: &#039;க&#039;, &#039;घ&#039;: &#039;க&#039;, &#039;ङ&#039;: &#039;ங&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ச&#039;, &#039;छ&#039;: &#039;ச&#039;, &#039;ज&#039;: &#039;ஜ&#039;, &#039;झ&#039;: &#039;ஜ&#039;, &#039;ञ&#039;: &#039;ஞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ட&#039;, &#039;ठ&#039;: &#039;ட&#039;, &#039;ड&#039;: &#039;ட&#039;, &#039;ढ&#039;: &#039;ட&#039;, &#039;ण&#039;: &#039;ண&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;த&#039;, &#039;थ&#039;: &#039;த&#039;, &#039;द&#039;: &#039;த&#039;, &#039;ध&#039;: &#039;த&#039;, &#039;न&#039;: &#039;ந&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ப&#039;, &#039;फ&#039;: &#039;ப&#039;, &#039;ब&#039;: &#039;ப&#039;, &#039;भ&#039;: &#039;ப&#039;, &#039;म&#039;: &#039;ம&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ய&#039;, &#039;र&#039;: &#039;ர&#039;, &#039;ल&#039;: &#039;ல&#039;, &#039;ळ&#039;: &#039;ள&#039;, &#039;व&#039;: &#039;வ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ஶ&#039;, &#039;ष&#039;: &#039;ஷ&#039;, &#039;स&#039;: &#039;ஸ&#039;, &#039;ह&#039;: &#039;ஹ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ா&#039;, &#039;ि&#039;: &#039;ி&#039;, &#039;ी&#039;: &#039;ீ&#039;, &#039;ु&#039;: &#039;ு&#039;, &#039;ू&#039;: &#039;ூ&#039;,&lt;br /&gt;
            &#039;े&#039;: &#039;ே&#039;, &#039;ै&#039;: &#039;ை&#039;, &#039;ो&#039;: &#039;ோ&#039;, &#039;ौ&#039;: &#039;ௌ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;அ&#039;, &#039;आ&#039;: &#039;ஆ&#039;, &#039;इ&#039;: &#039;இ&#039;, &#039;ई&#039;: &#039;ஈ&#039;, &#039;उ&#039;: &#039;உ&#039;, &#039;ऊ&#039;: &#039;ஊ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ஏ&#039;, &#039;ऐ&#039;: &#039;ஐ&#039;, &#039;ओ&#039;: &#039;ஓ&#039;, &#039;औ&#039;: &#039;ஔ&#039;, &#039;ऽ&#039;: &#039;ऽ&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;:&#039;ம்&#039;, &#039;ः&#039;:&#039;:&#039;, &#039;ँ&#039;:&#039;ம்&#039;, &#039;ॐ&#039;:&#039;ௐ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base + &#039;்&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ृ&#039;) { result += base + &#039;்ரி&#039;; i += 2; }&lt;br /&gt;
                else if (next === &#039;ॄ&#039;) { result += base + &#039;்ரீ&#039;; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else { result += base; i++; }   // inherent &#039;a&#039;&lt;br /&gt;
            }&lt;br /&gt;
            else if (ch === &#039;ऋ&#039;) { result += &#039;ரி&#039;; i++; }&lt;br /&gt;
            else if (ch === &#039;ॠ&#039;) { result += &#039;ரீ&#039;; i++; }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function devanagariToIAST(text) {&lt;br /&gt;
        var CONSONANTS = {&lt;br /&gt;
            &#039;क&#039;: &#039;k&#039;, &#039;ख&#039;: &#039;kh&#039;, &#039;ग&#039;: &#039;g&#039;, &#039;घ&#039;: &#039;gh&#039;, &#039;ङ&#039;: &#039;ṅ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;c&#039;, &#039;छ&#039;: &#039;ch&#039;, &#039;ज&#039;: &#039;j&#039;, &#039;झ&#039;: &#039;jh&#039;, &#039;ञ&#039;: &#039;ñ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ṭ&#039;, &#039;ठ&#039;: &#039;ṭh&#039;, &#039;ड&#039;: &#039;ḍ&#039;, &#039;ढ&#039;: &#039;ḍh&#039;, &#039;ण&#039;: &#039;ṇ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;t&#039;, &#039;थ&#039;: &#039;th&#039;, &#039;द&#039;: &#039;d&#039;, &#039;ध&#039;: &#039;dh&#039;, &#039;न&#039;: &#039;n&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;p&#039;, &#039;फ&#039;: &#039;ph&#039;, &#039;ब&#039;: &#039;b&#039;, &#039;भ&#039;: &#039;bh&#039;, &#039;म&#039;: &#039;m&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;y&#039;, &#039;र&#039;: &#039;r&#039;, &#039;ल&#039;: &#039;l&#039;, &#039;ळ&#039;: &#039;ḷ&#039;, &#039;व&#039;: &#039;v&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ś&#039;, &#039;ष&#039;: &#039;ṣ&#039;, &#039;स&#039;: &#039;s&#039;, &#039;ह&#039;: &#039;h&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var DIACRITICS = {&lt;br /&gt;
            &#039;ा&#039;: &#039;ā&#039;, &#039;ि&#039;: &#039;i&#039;, &#039;ी&#039;: &#039;ī&#039;, &#039;ु&#039;: &#039;u&#039;, &#039;ू&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ṛ&#039;, &#039;ॄ&#039;: &#039;ṝ&#039;, &#039;े&#039;: &#039;e&#039;, &#039;ै&#039;: &#039;ai&#039;, &#039;ो&#039;: &#039;o&#039;, &#039;ौ&#039;: &#039;au&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var VOWELS = {&lt;br /&gt;
            &#039;अ&#039;: &#039;a&#039;, &#039;आ&#039;: &#039;ā&#039;, &#039;इ&#039;: &#039;i&#039;, &#039;ई&#039;: &#039;ī&#039;, &#039;उ&#039;: &#039;u&#039;, &#039;ऊ&#039;: &#039;ū&#039;,&lt;br /&gt;
            &#039;ऋ&#039;: &#039;ṛ&#039;, &#039;ॠ&#039;: &#039;ṝ&#039;, &#039;ए&#039;: &#039;e&#039;, &#039;ऐ&#039;: &#039;ai&#039;, &#039;ओ&#039;: &#039;o&#039;, &#039;औ&#039;: &#039;au&#039;, &#039;ऽ&#039;: &amp;quot;&#039;&amp;quot;&lt;br /&gt;
        };&lt;br /&gt;
        var MISC = {&lt;br /&gt;
            &#039;ं&#039;: &#039;ṃ&#039;, &#039;ः&#039;: &#039;ḥ&#039;, &#039;ँ&#039;: &#039;m̐&#039;, &#039;ॐ&#039;: &#039;oṃ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;0&#039;, &#039;१&#039;: &#039;1&#039;, &#039;२&#039;: &#039;2&#039;, &#039;३&#039;: &#039;3&#039;, &#039;४&#039;: &#039;4&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;5&#039;, &#039;६&#039;: &#039;6&#039;, &#039;७&#039;: &#039;7&#039;, &#039;८&#039;: &#039;8&#039;, &#039;९&#039;: &#039;9&#039;&lt;br /&gt;
        };&lt;br /&gt;
        var HALANTA = &#039;्&#039;;&lt;br /&gt;
        var chars = Array.from(text), result = &#039;&#039;, i = 0;&lt;br /&gt;
        while (i &amp;lt; chars.length) {&lt;br /&gt;
            var ch = chars[i], next = chars[i + 1];&lt;br /&gt;
            if (CONSONANTS[ch]) {&lt;br /&gt;
                var base = CONSONANTS[ch];&lt;br /&gt;
                if (next === HALANTA) { result += base; i += 2; }&lt;br /&gt;
                else if (DIACRITICS[next]) { result += base + DIACRITICS[next]; i += 2; }&lt;br /&gt;
                else if (next === &#039;ं&#039; || next === &#039;ः&#039;) { result += base + &#039;a&#039; + MISC[next]; i += 2; }&lt;br /&gt;
                else { result += base + &#039;a&#039;; i++; }&lt;br /&gt;
            }&lt;br /&gt;
            else if (VOWELS[ch]) { result += VOWELS[ch]; i++; }&lt;br /&gt;
            else if (DIACRITICS[ch]) { result += DIACRITICS[ch]; i++; }&lt;br /&gt;
            else if (MISC[ch]) { result += MISC[ch]; i++; }&lt;br /&gt;
            else { result += ch; i++; }&lt;br /&gt;
        }&lt;br /&gt;
        return result;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var SCRIPT_MAP = {&lt;br /&gt;
        kn: {&lt;br /&gt;
            &#039;अ&#039;: &#039;ಅ&#039;, &#039;आ&#039;: &#039;ಆ&#039;, &#039;इ&#039;: &#039;ಇ&#039;, &#039;ई&#039;: &#039;ಈ&#039;, &#039;उ&#039;: &#039;ಉ&#039;, &#039;ऊ&#039;: &#039;ಊ&#039;, &#039;ऋ&#039;: &#039;ಋ&#039;, &#039;ॠ&#039;: &#039;ೠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ಏ&#039;, &#039;ऐ&#039;: &#039;ಐ&#039;, &#039;ओ&#039;: &#039;ಓ&#039;, &#039;औ&#039;: &#039;ಔ&#039;, &#039;ऽ&#039;: &#039;ಽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;ಕ&#039;, &#039;ख&#039;: &#039;ಖ&#039;, &#039;ग&#039;: &#039;ಗ&#039;, &#039;घ&#039;: &#039;ಘ&#039;, &#039;ङ&#039;: &#039;ಙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;ಚ&#039;, &#039;छ&#039;: &#039;ಛ&#039;, &#039;ज&#039;: &#039;ಜ&#039;, &#039;झ&#039;: &#039;ಝ&#039;, &#039;ञ&#039;: &#039;ಞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ಟ&#039;, &#039;ठ&#039;: &#039;ಠ&#039;, &#039;ड&#039;: &#039;ಡ&#039;, &#039;ढ&#039;: &#039;ಢ&#039;, &#039;ण&#039;: &#039;ಣ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;ತ&#039;, &#039;थ&#039;: &#039;ಥ&#039;, &#039;द&#039;: &#039;ದ&#039;, &#039;ध&#039;: &#039;ಧ&#039;, &#039;न&#039;: &#039;ನ&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ಪ&#039;, &#039;फ&#039;: &#039;ಫ&#039;, &#039;ब&#039;: &#039;ಬ&#039;, &#039;भ&#039;: &#039;ಭ&#039;, &#039;म&#039;: &#039;ಮ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;ಯ&#039;, &#039;र&#039;: &#039;ರ&#039;, &#039;ल&#039;: &#039;ಲ&#039;, &#039;ळ&#039;: &#039;ಳ&#039;, &#039;व&#039;: &#039;ವ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;ಶ&#039;, &#039;ष&#039;: &#039;ಷ&#039;, &#039;स&#039;: &#039;ಸ&#039;, &#039;ह&#039;: &#039;ಹ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ಾ&#039;, &#039;ि&#039;: &#039;ಿ&#039;, &#039;ी&#039;: &#039;ೀ&#039;, &#039;ु&#039;: &#039;ು&#039;, &#039;ू&#039;: &#039;ೂ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ೃ&#039;, &#039;ॄ&#039;: &#039;ೄ&#039;, &#039;े&#039;: &#039;ೇ&#039;, &#039;ै&#039;: &#039;ೈ&#039;, &#039;ो&#039;: &#039;ೋ&#039;, &#039;ौ&#039;: &#039;ೌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ಂ&#039;, &#039;ः&#039;: &#039;ಃ&#039;, &#039;्&#039;: &#039;್&#039;, &#039;ँ&#039;: &#039;ಁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;೦&#039;, &#039;१&#039;: &#039;೧&#039;, &#039;२&#039;: &#039;೨&#039;, &#039;३&#039;: &#039;೩&#039;, &#039;४&#039;: &#039;೪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;೫&#039;, &#039;६&#039;: &#039;೬&#039;, &#039;७&#039;: &#039;೭&#039;, &#039;८&#039;: &#039;೮&#039;, &#039;९&#039;: &#039;೯&#039;&lt;br /&gt;
        },&lt;br /&gt;
        te: {&lt;br /&gt;
            &#039;अ&#039;: &#039;అ&#039;, &#039;आ&#039;: &#039;ఆ&#039;, &#039;इ&#039;: &#039;ఇ&#039;, &#039;ई&#039;: &#039;ఈ&#039;, &#039;उ&#039;: &#039;ఉ&#039;, &#039;ऊ&#039;: &#039;ఊ&#039;, &#039;ऋ&#039;: &#039;ఋ&#039;, &#039;ॠ&#039;: &#039;ౠ&#039;,&lt;br /&gt;
            &#039;ए&#039;: &#039;ఏ&#039;, &#039;ऐ&#039;: &#039;ఐ&#039;, &#039;ओ&#039;: &#039;ఓ&#039;, &#039;औ&#039;: &#039;ఔ&#039;, &#039;ऽ&#039;: &#039;ఽ&#039;,&lt;br /&gt;
            &#039;क&#039;: &#039;క&#039;, &#039;ख&#039;: &#039;ఖ&#039;, &#039;ग&#039;: &#039;గ&#039;, &#039;घ&#039;: &#039;ఘ&#039;, &#039;ङ&#039;: &#039;ఙ&#039;,&lt;br /&gt;
            &#039;च&#039;: &#039;చ&#039;, &#039;छ&#039;: &#039;ఛ&#039;, &#039;ज&#039;: &#039;జ&#039;, &#039;झ&#039;: &#039;ఝ&#039;, &#039;ञ&#039;: &#039;ఞ&#039;,&lt;br /&gt;
            &#039;ट&#039;: &#039;ట&#039;, &#039;ठ&#039;: &#039;ఠ&#039;, &#039;ड&#039;: &#039;డ&#039;, &#039;ढ&#039;: &#039;ఢ&#039;, &#039;ण&#039;: &#039;ణ&#039;,&lt;br /&gt;
            &#039;त&#039;: &#039;త&#039;, &#039;थ&#039;: &#039;థ&#039;, &#039;द&#039;: &#039;ద&#039;, &#039;ध&#039;: &#039;ధ&#039;, &#039;न&#039;: &#039;న&#039;,&lt;br /&gt;
            &#039;प&#039;: &#039;ప&#039;, &#039;फ&#039;: &#039;ఫ&#039;, &#039;ब&#039;: &#039;బ&#039;, &#039;भ&#039;: &#039;భ&#039;, &#039;म&#039;: &#039;మ&#039;,&lt;br /&gt;
            &#039;य&#039;: &#039;య&#039;, &#039;र&#039;: &#039;ర&#039;, &#039;ल&#039;: &#039;ల&#039;, &#039;ळ&#039;: &#039;ళ&#039;, &#039;व&#039;: &#039;వ&#039;,&lt;br /&gt;
            &#039;श&#039;: &#039;శ&#039;, &#039;ष&#039;: &#039;ష&#039;, &#039;स&#039;: &#039;స&#039;, &#039;ह&#039;: &#039;హ&#039;,&lt;br /&gt;
            &#039;ा&#039;: &#039;ా&#039;, &#039;ि&#039;: &#039;ి&#039;, &#039;ी&#039;: &#039;ీ&#039;, &#039;ु&#039;: &#039;ు&#039;, &#039;ू&#039;: &#039;ూ&#039;,&lt;br /&gt;
            &#039;ृ&#039;: &#039;ృ&#039;, &#039;ॄ&#039;: &#039;ౄ&#039;, &#039;े&#039;: &#039;ే&#039;, &#039;ै&#039;: &#039;ై&#039;, &#039;ो&#039;: &#039;ో&#039;, &#039;ौ&#039;: &#039;ౌ&#039;,&lt;br /&gt;
            &#039;ं&#039;: &#039;ం&#039;, &#039;ः&#039;: &#039;ః&#039;, &#039;्&#039;: &#039;్&#039;, &#039;ँ&#039;: &#039;ఁ&#039;,&lt;br /&gt;
            &#039;०&#039;: &#039;౦&#039;, &#039;१&#039;: &#039;౧&#039;, &#039;२&#039;: &#039;౨&#039;, &#039;३&#039;: &#039;౩&#039;, &#039;४&#039;: &#039;౪&#039;,&lt;br /&gt;
            &#039;५&#039;: &#039;౫&#039;, &#039;६&#039;: &#039;౬&#039;, &#039;७&#039;: &#039;౭&#039;, &#039;८&#039;: &#039;౮&#039;, &#039;९&#039;: &#039;౯&#039;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var PRE = [&lt;br /&gt;
        [/ङ्क/g, &#039;ंक&#039;], [/ङ्ख/g, &#039;ंख&#039;], [/ङ्ग/g, &#039;ंग&#039;], [/ङ्घ/g, &#039;ंघ&#039;],&lt;br /&gt;
        [/ञ्च/g, &#039;ंच&#039;], [/ञ्ज/g, &#039;ंज&#039;], [/ण्ट/g, &#039;ंट&#039;], [/ण्ड/g, &#039;ंड&#039;],&lt;br /&gt;
        [/न्त/g, &#039;ंत&#039;], [/न्द/g, &#039;ंद&#039;], [/म्ब/g, &#039;ंब&#039;], [/म्भ/g, &#039;ंभ&#039;]&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function transliterateText(text, script) {&lt;br /&gt;
        if (script === &#039;en&#039;) return devanagariToIAST(text);&lt;br /&gt;
        if (script === &#039;ta&#039;) return devanagariToTamil(text);&lt;br /&gt;
        var map = SCRIPT_MAP[script];&lt;br /&gt;
        if (!map) return text;&lt;br /&gt;
        var t = text;&lt;br /&gt;
        PRE.forEach(function (p) { t = t.replace(p[0], p[1]); });&lt;br /&gt;
        return Array.from(t).map(function (ch) {&lt;br /&gt;
            return map[ch] !== undefined ? map[ch] : ch;&lt;br /&gt;
        }).join(&#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var translatableSpans = [];&lt;br /&gt;
&lt;br /&gt;
    function tagTextNodes() {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (content) {&lt;br /&gt;
            var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
            var nodes = [];&lt;br /&gt;
            while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
            nodes.forEach(function (node) {&lt;br /&gt;
                var p = node.parentNode;&lt;br /&gt;
                if (!p) return;&lt;br /&gt;
                if (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
                if (p.closest) {&lt;br /&gt;
                    if (p.closest(&#039;.gr-controls&#039;)) return;&lt;br /&gt;
                    if (p.closest(&#039;.mw-editsection&#039;)) return;&lt;br /&gt;
                }&lt;br /&gt;
                var orig = node.textContent;&lt;br /&gt;
                if (!orig.trim()) return;&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
                span.textContent = orig;&lt;br /&gt;
                p.replaceChild(span, node);&lt;br /&gt;
                translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        document.querySelectorAll(&#039;.vector-toc .vector-toc-text&#039;).forEach(function (span) {&lt;br /&gt;
            if (span.hasAttribute(&#039;data-deva&#039;)) return;&lt;br /&gt;
            var orig = span.textContent;&lt;br /&gt;
            if (!orig.trim()) return;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyScript(script) {&lt;br /&gt;
        currentScript = script;&lt;br /&gt;
        translatableSpans.forEach(function (span) {&lt;br /&gt;
            if (!span.parentNode) return;&lt;br /&gt;
            var orig = span.getAttribute(&#039;data-deva&#039;);&lt;br /&gt;
            if (!orig) return;&lt;br /&gt;
            span.textContent = (script === &#039;deva&#039;)&lt;br /&gt;
                ? orig&lt;br /&gt;
                : transliterateText(orig, script);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function _isNoTocPage() {&lt;br /&gt;
        var pn = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        return pn === &#039;Main_Page&#039; || /^[A-Za-z0-9_]+:About$/.test(pn);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renameTocTitle() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var titleEl = toc.querySelector(&#039;.vector-toc-title&#039;) || toc.querySelector(&#039;.vector-pinnable-header-label&#039;);&lt;br /&gt;
        if (!titleEl) return;&lt;br /&gt;
        var LABEL = &#039;विषयसूची&#039;;&lt;br /&gt;
        var span = titleEl.querySelector(&#039;.gr-toc-title&#039;);&lt;br /&gt;
        if (!span) {&lt;br /&gt;
            titleEl.innerHTML = &#039;&#039;;&lt;br /&gt;
            span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.className = &#039;gr-toc-title&#039;;&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, LABEL);&lt;br /&gt;
            titleEl.appendChild(span);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        }&lt;br /&gt;
        span.textContent = currentScript === &#039;deva&#039; ? LABEL : transliterateText(LABEL, currentScript);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function removeTocBeginning() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var el = toc.querySelector(&#039;#vector-toc-beginning&#039;);&lt;br /&gt;
        if (!el) {&lt;br /&gt;
            var items = toc.querySelectorAll(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; items.length; i++) {&lt;br /&gt;
                var a = items[i].querySelector(&#039;a&#039;);&lt;br /&gt;
                if (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (href.indexOf(&#039;#&#039;) === -1) { el = items[i]; break; }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The commentary list at the top of a moola page has its own heading&lt;br /&gt;
    // (टीकाः). It isn&#039;t part of the text, so it doesn&#039;t belong in the&lt;br /&gt;
    // contents. Recognised by what the section holds — the .gr-doc-teekas&lt;br /&gt;
    // list, or a section whose only links are to /Vyakhya/ pages — with&lt;br /&gt;
    // the heading&#039;s own wording as a last resort, since the list markup&lt;br /&gt;
    // differs between pages and the commentary picker rewrites it.&lt;br /&gt;
    var TEEKA_HEADINGS = /^(टीकाः?|व्याख्याः?|टीकाग्रन्थाः|Teekas?|Commentaries)$/i;&lt;br /&gt;
    function isTeekaSection(headingEl) {&lt;br /&gt;
        var wrap = (headingEl.closest &amp;amp;&amp;amp; headingEl.closest(&#039;.mw-heading&#039;)) || headingEl;&lt;br /&gt;
        var n = wrap.nextElementSibling, links = [], vyakhya = 0, steps = 0;&lt;br /&gt;
        while (n &amp;amp;&amp;amp; steps &amp;lt; 6) {&lt;br /&gt;
            if (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if (n.querySelector &amp;amp;&amp;amp; n.querySelector(&#039;.gr-doc-teekas&#039;)) return true;&lt;br /&gt;
            if ((n.tagName &amp;amp;&amp;amp; /^H[1-6]$/.test(n.tagName)) || (n.classList &amp;amp;&amp;amp; n.classList.contains(&#039;mw-heading&#039;))) break;&lt;br /&gt;
            if (n.querySelectorAll) {&lt;br /&gt;
                Array.prototype.forEach.call(n.querySelectorAll(&#039;a[href]&#039;), function (a) {&lt;br /&gt;
                    var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
                    if (/^#/.test(href)) return;&lt;br /&gt;
                    links.push(href);&lt;br /&gt;
                    if (/\/Vyakhya\//i.test(decodeURIComponent(href))) vyakhya++;&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
            n = n.nextElementSibling; steps++;&lt;br /&gt;
        }&lt;br /&gt;
        if (links.length &amp;amp;&amp;amp; vyakhya === links.length) return true;&lt;br /&gt;
        return TEEKA_HEADINGS.test((headingEl.textContent || &#039;&#039;).trim());&lt;br /&gt;
    }&lt;br /&gt;
    function removeTocTeekaEntry() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        Array.prototype.forEach.call(toc.querySelectorAll(&#039;.vector-toc-link[href^=&amp;quot;#&amp;quot;]&#039;), function (a) {&lt;br /&gt;
            var id = a.getAttribute(&#039;href&#039;).slice(1);&lt;br /&gt;
            try { id = decodeURIComponent(id); } catch (e) { }&lt;br /&gt;
            var h = id &amp;amp;&amp;amp; document.getElementById(id);&lt;br /&gt;
            if (!h || !isTeekaSection(h)) return;&lt;br /&gt;
            var li = a.closest(&#039;.vector-toc-list-item&#039;);&lt;br /&gt;
            if (li &amp;amp;&amp;amp; li.parentNode) li.parentNode.removeChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function expandTocSections() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.vector-toc-list-item-collapsed&#039;).forEach(function (li) {&lt;br /&gt;
            li.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTocDocNav() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        if (document.getElementById(&#039;gr-toc-doc-nav&#039;)) return;&lt;br /&gt;
        var artPath = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        var pageTitle = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || &#039;&#039;) : &#039;&#039;;&lt;br /&gt;
        if (!primarySlug) { primarySlug = pageTitle.split(&#039;/&#039;)[0]; }&lt;br /&gt;
        if (!primarySlug) return;&lt;br /&gt;
        function wikiUrl(slug) {&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug);&lt;br /&gt;
            return artPath.replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolaPage = !teekaPage &amp;amp;&amp;amp; hasMoolaPage;&lt;br /&gt;
        var showMoolaBack = !!teekaPage;&lt;br /&gt;
        var showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolaPage &amp;amp;&amp;amp; !showMoolaBack &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
        nav.id = &#039;gr-toc-doc-nav&#039;;&lt;br /&gt;
        nav.setAttribute(&#039;class&#039;, &#039;toc-main-links&#039;);&lt;br /&gt;
        function makeBtn(href, label) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href;&lt;br /&gt;
            a.setAttribute(&#039;class&#039;, &#039;toc-main-link-item&#039;);&lt;br /&gt;
            var lspan = document.createElement(&#039;span&#039;);&lt;br /&gt;
            lspan.setAttribute(&#039;data-deva&#039;, label);&lt;br /&gt;
            lspan.textContent = (currentScript &amp;amp;&amp;amp; currentScript !== &#039;deva&#039;) ? transliterateText(label, currentScript) : label;&lt;br /&gt;
            translatableSpans.push(lspan);&lt;br /&gt;
            a.appendChild(lspan);&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
        if (showMoolaPage) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showMoolaBack) nav.appendChild(makeBtn(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeBtn(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Under the document&#039;s own title in the page, the same as on a&lt;br /&gt;
        // phone — not tucked into the contents panel, where these were&lt;br /&gt;
        // easy to miss. The contents panel is only the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) { docTitle.parentNode.insertBefore(nav, docTitle.nextSibling); return; }&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.mw-first-heading&#039;);&lt;br /&gt;
        if (h1 &amp;amp;&amp;amp; h1.parentNode) { h1.parentNode.insertBefore(nav, h1.nextSibling); return; }&lt;br /&gt;
        var tocContents = toc.querySelector(&#039;.vector-toc-contents&#039;);&lt;br /&gt;
        if (tocContents) toc.insertBefore(nav, tocContents);&lt;br /&gt;
        else toc.appendChild(nav);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var _headingObserver = null;&lt;br /&gt;
    var _tocActiveId = null;&lt;br /&gt;
&lt;br /&gt;
    // NEW: moved out of attachHeadingObserver&#039;s own local scope so they&lt;br /&gt;
    // can be reused both by the scroll observer AND by a direct trigger&lt;br /&gt;
    // for search-result clicks — was previously only reachable from&lt;br /&gt;
    // inside attachHeadingObserver&#039;s own IntersectionObserver callback.&lt;br /&gt;
    // _tocActiveId (was _activeId, local to attachHeadingObserver) is&lt;br /&gt;
    // module-level now for the same reason — both callers need to share&lt;br /&gt;
    // the same &amp;quot;what&#039;s currently active&amp;quot; state, not track it separately.&lt;br /&gt;
    function getTocLink(id) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return null;&lt;br /&gt;
        return toc.querySelector(&#039;a[href=&amp;quot;#&#039; + CSS.escape(id) + &#039;&amp;quot;]&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function getTocLi(id) {&lt;br /&gt;
        var a = getTocLink(id);&lt;br /&gt;
        return a ? a.closest(&#039;.vector-toc-list-item&#039;) : null;&lt;br /&gt;
    }&lt;br /&gt;
    // Highlighting is class-only (styled in Common.css §6):&lt;br /&gt;
    //   .gr-toc-current  — the one entry being read, on the &amp;lt;a&amp;gt; itself&lt;br /&gt;
    //   .gr-toc-in-path  — its parent sections, marked on the &amp;lt;li&amp;gt;&lt;br /&gt;
    // Previously this set inline colours on the active item AND on every&lt;br /&gt;
    // element inside it, so a section with sub-sections lit up whole.&lt;br /&gt;
    function clearActiveToc() {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-current&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-current&#039;); });&lt;br /&gt;
        toc.querySelectorAll(&#039;.gr-toc-in-path&#039;).forEach(function (el) { el.classList.remove(&#039;gr-toc-in-path&#039;); });&lt;br /&gt;
    }&lt;br /&gt;
    function ownLink(li) {&lt;br /&gt;
        return li.querySelector(&#039;:scope &amp;gt; .vector-toc-link&#039;) || li.querySelector(&#039;.vector-toc-link&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    function isShown(el) {&lt;br /&gt;
        return !!(el &amp;amp;&amp;amp; (el.offsetParent || (el.getClientRects &amp;amp;&amp;amp; el.getClientRects().length)));&lt;br /&gt;
    }&lt;br /&gt;
    // Open a collapsed section so a given entry can be seen. Vector marks a&lt;br /&gt;
    // top-level item open with vector-toc-list-item-expanded plus&lt;br /&gt;
    // aria-expanded on its toggle, so this matches what its own button does.&lt;br /&gt;
    function expandTocFor(li) {&lt;br /&gt;
        var top = li.closest ? li.closest(&#039;.vector-toc-level-1&#039;) : null;&lt;br /&gt;
        if (!top) return;&lt;br /&gt;
        if (!top.classList.contains(&#039;vector-toc-list-item-expanded&#039;)) {&lt;br /&gt;
            top.classList.add(&#039;vector-toc-list-item-expanded&#039;);&lt;br /&gt;
            var tg = top.querySelector(&#039;.vector-toc-toggle&#039;);&lt;br /&gt;
            if (tg) tg.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);&lt;br /&gt;
        }&lt;br /&gt;
        var anc = li.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc.classList) {&lt;br /&gt;
            anc.classList.remove(&#039;vector-toc-list-item-collapsed&#039;);&lt;br /&gt;
            if (anc.classList.contains(&#039;vector-toc&#039;)) break;&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // expand=true when the reader picked the entry (TOC click, search&lt;br /&gt;
    // result, #hash): open its section so the exact entry is shown.&lt;br /&gt;
    // expand=false while scrolling: never open or close anything — if the&lt;br /&gt;
    // entry is hidden inside a collapsed section, the section itself is&lt;br /&gt;
    // highlighted instead, so the reader still sees where they are.&lt;br /&gt;
    function setActiveToc(id, expand) {&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        var shownNow = toc.querySelector(&#039;.gr-toc-current&#039;);&lt;br /&gt;
        if (_tocActiveId === id &amp;amp;&amp;amp; !expand &amp;amp;&amp;amp; shownNow &amp;amp;&amp;amp; isShown(shownNow)) return;&lt;br /&gt;
        _tocActiveId = id;&lt;br /&gt;
        clearActiveToc();&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        var li = getTocLi(id);&lt;br /&gt;
        if (!li) return;&lt;br /&gt;
        if (expand) expandTocFor(li);&lt;br /&gt;
&lt;br /&gt;
        var lnk = ownLink(li);&lt;br /&gt;
        var hostLi = li;&lt;br /&gt;
        if (lnk &amp;amp;&amp;amp; !isShown(lnk)) {          // hidden in a collapsed section&lt;br /&gt;
            var p = li.parentNode;&lt;br /&gt;
            while (p &amp;amp;&amp;amp; p !== toc) {&lt;br /&gt;
                if (p.classList &amp;amp;&amp;amp; p.classList.contains(&#039;vector-toc-list-item&#039;)) {&lt;br /&gt;
                    var pl = ownLink(p);&lt;br /&gt;
                    if (pl &amp;amp;&amp;amp; isShown(pl)) { lnk = pl; hostLi = p; break; }&lt;br /&gt;
                }&lt;br /&gt;
                p = p.parentNode;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if (lnk) lnk.classList.add(&#039;gr-toc-current&#039;);&lt;br /&gt;
        // Sections above it get the quieter &amp;quot;you are in here&amp;quot; treatment.&lt;br /&gt;
        var anc = hostLi.parentNode;&lt;br /&gt;
        while (anc &amp;amp;&amp;amp; anc !== toc) {&lt;br /&gt;
            if (anc.classList &amp;amp;&amp;amp; anc.classList.contains(&#039;vector-toc-list-item&#039;)) anc.classList.add(&#039;gr-toc-in-path&#039;);&lt;br /&gt;
            anc = anc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        keepTocEntryVisible(lnk || hostLi);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Scroll the TOC panel so the active entry is visible. This used to&lt;br /&gt;
    // measure the whole &amp;lt;li&amp;gt;, which for a section with sub-sections is as&lt;br /&gt;
    // tall as the entire branch — so &amp;quot;centre it&amp;quot; landed somewhere inside&lt;br /&gt;
    // the NEXT section. It now measures the link itself, and finds the&lt;br /&gt;
    // element that actually scrolls rather than assuming one.&lt;br /&gt;
    function keepTocEntryVisible(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var sc = el.parentNode;&lt;br /&gt;
        while (sc &amp;amp;&amp;amp; sc !== document.body &amp;amp;&amp;amp; sc.nodeType === 1) {&lt;br /&gt;
            var oy = window.getComputedStyle(sc).overflowY;&lt;br /&gt;
            if ((oy === &#039;auto&#039; || oy === &#039;scroll&#039;) &amp;amp;&amp;amp; sc.scrollHeight &amp;gt; sc.clientHeight + 4) break;&lt;br /&gt;
            sc = sc.parentNode;&lt;br /&gt;
        }&lt;br /&gt;
        if (!sc || sc === document.body || sc.nodeType !== 1) return;   // nothing scrolls: leave the page alone&lt;br /&gt;
        var er = el.getBoundingClientRect(), cr = sc.getBoundingClientRect();&lt;br /&gt;
        if (er.top &amp;gt;= cr.top + 8 &amp;amp;&amp;amp; er.bottom &amp;lt;= cr.bottom - 8) return; // already in view&lt;br /&gt;
        sc.scrollTop += (er.top - cr.top) - Math.max((sc.clientHeight - er.height) / 2, 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: given any element in the article content, finds the nearest&lt;br /&gt;
    // heading (with an id) that precedes it in document order, using&lt;br /&gt;
    // compareDocumentPosition — robust regardless of nesting depth.&lt;br /&gt;
    // Verified against a realistic multi-section fragment, including&lt;br /&gt;
    // the &amp;quot;before any heading&amp;quot; and &amp;quot;right after a chapter heading, no&lt;br /&gt;
    // sub-heading yet&amp;quot; edge cases, before wiring this in.&lt;br /&gt;
    function findNearestHeadingId(target) {&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return null;&lt;br /&gt;
        var headings = content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;);&lt;br /&gt;
        var nearest = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; headings.length; i++) {&lt;br /&gt;
            var h = headings[i];&lt;br /&gt;
            var pos = h.compareDocumentPosition(target);&lt;br /&gt;
            if (pos &amp;amp; Node.DOCUMENT_POSITION_FOLLOWING) {&lt;br /&gt;
                nearest = h.id;&lt;br /&gt;
            } else {&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return nearest;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // NEW: exposed so the search-highlight IIFE (below) can directly&lt;br /&gt;
    // trigger the TOC to expand and highlight the right section right&lt;br /&gt;
    // after a search-result scroll lands — rather than relying solely on&lt;br /&gt;
    // the IntersectionObserver to catch up on its own timing.&lt;br /&gt;
    window.grHighlightTocForElement = function (el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        var id = findNearestHeadingId(el);&lt;br /&gt;
        if (id) pinActive(id);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // ── Which section am I reading? ────────────────────────────────&lt;br /&gt;
    // Was an IntersectionObserver with a shrunken root band. Two faults&lt;br /&gt;
    // came out of that, both reported as &amp;quot;the highlight is wrong&amp;quot;:&lt;br /&gt;
    //   • right after jumping to a heading, that heading sits ABOVE the&lt;br /&gt;
    //     band, so nothing was &amp;quot;visible&amp;quot; and the fallback picked the&lt;br /&gt;
    //     PREVIOUS heading — clicking a sub-section highlighted its parent;&lt;br /&gt;
    //   • once every heading was outside the band, no intersection ever&lt;br /&gt;
    //     changed again, so the observer stopped firing and the highlight&lt;br /&gt;
    //     froze wherever it was.&lt;br /&gt;
    // This measures instead: the active section is the last heading above&lt;br /&gt;
    // a reading line just under the sticky toolbar. It updates on every&lt;br /&gt;
    // scroll frame and can&#039;t go stale.&lt;br /&gt;
    var _tocHeadings = [];&lt;br /&gt;
    var _tocTops = [];&lt;br /&gt;
    var _tocMeasuredHeight = 0;&lt;br /&gt;
    // A clicked / jumped-to heading is &amp;quot;pinned&amp;quot;: it stays the active entry&lt;br /&gt;
    // until the reader actually scrolls away from it. Without this, the&lt;br /&gt;
    // anchor jump leaves the heading below the reading line (the sticky&lt;br /&gt;
    // bar and scroll-margin push it down the viewport), so the measurement&lt;br /&gt;
    // picked the heading ABOVE it and the highlight jumped back one.&lt;br /&gt;
    var _pin = null;                                  // {id, y, until}&lt;br /&gt;
&lt;br /&gt;
    function measureHeadings() {&lt;br /&gt;
        var y = window.pageYOffset || document.documentElement.scrollTop || 0;&lt;br /&gt;
        _tocTops = _tocHeadings.map(function (h) { return h.getBoundingClientRect().top + y; });&lt;br /&gt;
        _tocMeasuredHeight = document.documentElement.scrollHeight;&lt;br /&gt;
    }&lt;br /&gt;
    function scrollY() { return window.pageYOffset || document.documentElement.scrollTop || 0; }&lt;br /&gt;
    // The line down the page that decides &amp;quot;which section am I reading&amp;quot;:&lt;br /&gt;
    // just below whatever is stuck to the top, plus a quarter of the&lt;br /&gt;
    // screen, so a heading sitting near the top of the viewport counts as&lt;br /&gt;
    // the one being read rather than the one before it.&lt;br /&gt;
    function readingLine() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return scrollY() + barBottom + Math.max(Math.round(vh * 0.25), 80);&lt;br /&gt;
    }&lt;br /&gt;
    // Scroll something into a comfortable reading position and KEEP it&lt;br /&gt;
    // there. A plain scrollIntoView lands short or long whenever the page&lt;br /&gt;
    // reflows just after the jump — a web font finishing, a commentary&lt;br /&gt;
    // being interleaved, the match itself being wrapped in a highlight —&lt;br /&gt;
    // which is why search results came to rest slightly above or below&lt;br /&gt;
    // the text. This aims just under whatever is stuck to the top, then&lt;br /&gt;
    // re-checks until the page has settled, and gives up the moment the&lt;br /&gt;
    // reader scrolls, so it never fights them.&lt;br /&gt;
    function grDesiredTop() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;);&lt;br /&gt;
        var barBottom = bar ? Math.max(bar.getBoundingClientRect().bottom, 0) : 0;&lt;br /&gt;
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;&lt;br /&gt;
        return barBottom + Math.max(Math.round(vh * 0.22), 72);&lt;br /&gt;
    }&lt;br /&gt;
    window.grScrollToElement = function (el) {&lt;br /&gt;
        if (!el || !el.getBoundingClientRect) return;&lt;br /&gt;
        var cancelled = false, ticks = 0, lastY = null, iv = null;&lt;br /&gt;
        function onUser() { cancelled = true; }&lt;br /&gt;
        function cleanup() {&lt;br /&gt;
            if (iv) clearInterval(iv);&lt;br /&gt;
            window.removeEventListener(&#039;wheel&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;touchstart&#039;, onUser);&lt;br /&gt;
            window.removeEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;wheel&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;touchstart&#039;, onUser, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;keydown&#039;, onUser);&lt;br /&gt;
        function delta() { return el.getBoundingClientRect().top - grDesiredTop(); }&lt;br /&gt;
        try { window.scrollBy({ top: delta(), left: 0, behavior: &#039;smooth&#039; }); }&lt;br /&gt;
        catch (e) { window.scrollBy(0, delta()); }&lt;br /&gt;
        iv = setInterval(function () {&lt;br /&gt;
            ticks++;&lt;br /&gt;
            if (cancelled || ticks &amp;gt; 12 || !document.body.contains(el)) { cleanup(); return; }&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (lastY !== null &amp;amp;&amp;amp; Math.abs(y - lastY) &amp;lt; 1) {     // the scroll has come to rest&lt;br /&gt;
                var d = delta();&lt;br /&gt;
                if (Math.abs(d) &amp;gt; 6) window.scrollBy(0, d);      // reflow moved it: correct at once&lt;br /&gt;
                else { cleanup(); return; }&lt;br /&gt;
            }&lt;br /&gt;
            lastY = y;&lt;br /&gt;
        }, 200);&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Called when the reader picks a heading (TOC click, search result,&lt;br /&gt;
    // or a #hash in the URL) — that heading wins until they scroll on.&lt;br /&gt;
    function pinActive(id) {&lt;br /&gt;
        if (!id) return;&lt;br /&gt;
        _pin = { id: id, y: scrollY(), until: Date.now() + 900 };&lt;br /&gt;
        setActiveToc(id, true);          // open its section if collapsed&lt;br /&gt;
        scheduleRecheck(950);&lt;br /&gt;
    }&lt;br /&gt;
    var _recheck = null;&lt;br /&gt;
    function scheduleRecheck(ms) {&lt;br /&gt;
        if (_recheck) return;&lt;br /&gt;
        _recheck = setTimeout(function () { _recheck = null; updateActiveFromScroll(); }, ms);&lt;br /&gt;
    }&lt;br /&gt;
    function updateActiveFromScroll() {&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        if (_pin) {&lt;br /&gt;
            var y = scrollY();&lt;br /&gt;
            if (Date.now() &amp;lt; _pin.until) {               // still settling after the jump&lt;br /&gt;
                _pin.y = y;&lt;br /&gt;
                setActiveToc(_pin.id, true);&lt;br /&gt;
                scheduleRecheck(_pin.until - Date.now() + 30);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            if (Math.abs(y - _pin.y) &amp;lt; 60) {            // reader hasn&#039;t moved off it&lt;br /&gt;
                setActiveToc(_pin.id);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            _pin = null;                                 // they&#039;ve scrolled on&lt;br /&gt;
        }&lt;br /&gt;
        // Interleaving a commentary or changing the font size moves things;&lt;br /&gt;
        // re-measure when the page&#039;s height has changed.&lt;br /&gt;
        if (document.documentElement.scrollHeight !== _tocMeasuredHeight) measureHeadings();&lt;br /&gt;
        var line = readingLine(), lo = 0, hi = _tocTops.length - 1, idx = 0;&lt;br /&gt;
        while (lo &amp;lt;= hi) {                                // last heading at or above the line&lt;br /&gt;
            var mid = (lo + hi) &amp;gt;&amp;gt; 1;&lt;br /&gt;
            if (_tocTops[mid] &amp;lt;= line + 1) { idx = mid; lo = mid + 1; } else { hi = mid - 1; }&lt;br /&gt;
        }&lt;br /&gt;
        setActiveToc(_tocHeadings[idx].id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function attachHeadingObserver() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        if (_headingObserver) return;&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
        if (!document.querySelector(&#039;.vector-toc&#039;)) return;&lt;br /&gt;
        // Only headings the contents lists — a heading with no entry (the&lt;br /&gt;
        // commentary list, say) would otherwise leave nothing highlighted.&lt;br /&gt;
        _tocHeadings = Array.prototype.slice.call(content.querySelectorAll(&#039;h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]&#039;))&lt;br /&gt;
            .filter(function (h) { return !!getTocLink(h.id); });&lt;br /&gt;
        if (!_tocHeadings.length) return;&lt;br /&gt;
        measureHeadings();&lt;br /&gt;
        // Time-based throttle rather than a requestAnimationFrame latch: if&lt;br /&gt;
        // one frame callback is ever skipped (background tab, a jump during&lt;br /&gt;
        // load) a latch stays stuck and the highlight stops updating for&lt;br /&gt;
        // good — which is one of the ways this went stale before.&lt;br /&gt;
        var lastRun = 0, trailing = null;&lt;br /&gt;
        function onScroll() {&lt;br /&gt;
            var now = Date.now();&lt;br /&gt;
            if (now - lastRun &amp;gt;= 80) { lastRun = now; updateActiveFromScroll(); }&lt;br /&gt;
            else if (!trailing) {&lt;br /&gt;
                trailing = setTimeout(function () { trailing = null; lastRun = Date.now(); updateActiveFromScroll(); }, 80);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        window.addEventListener(&#039;scroll&#039;, onScroll, { passive: true });&lt;br /&gt;
        window.addEventListener(&#039;resize&#039;, function () { measureHeadings(); onScroll(); }, { passive: true });&lt;br /&gt;
        if (document.fonts &amp;amp;&amp;amp; document.fonts.ready &amp;amp;&amp;amp; document.fonts.ready.then) {&lt;br /&gt;
            document.fonts.ready.then(function () { measureHeadings(); onScroll(); });&lt;br /&gt;
        }&lt;br /&gt;
        _headingObserver = { onScroll: onScroll };       // marks &amp;quot;already wired&amp;quot;&lt;br /&gt;
        var hash = &#039;&#039;;&lt;br /&gt;
        try { hash = decodeURIComponent((location.hash || &#039;&#039;).slice(1)); } catch (e) { hash = (location.hash || &#039;&#039;).slice(1); }&lt;br /&gt;
        if (hash &amp;amp;&amp;amp; document.getElementById(hash)) pinActive(hash);&lt;br /&gt;
        else updateActiveFromScroll();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clicking an entry highlights it straight away, rather than waiting&lt;br /&gt;
    // for the scroll observer to catch up after the jump.&lt;br /&gt;
    function attachTocClickHandler(toc) {&lt;br /&gt;
        if (toc.getAttribute(&#039;data-gr-click-wired&#039;)) return;&lt;br /&gt;
        toc.setAttribute(&#039;data-gr-click-wired&#039;, &#039;1&#039;);&lt;br /&gt;
        toc.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            var a = e.target.closest ? e.target.closest(&#039;.vector-toc-link&#039;) : null;&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            if (href.charAt(0) !== &#039;#&#039;) return;&lt;br /&gt;
            var id;&lt;br /&gt;
            try { id = decodeURIComponent(href.slice(1)); } catch (err) { id = href.slice(1); }&lt;br /&gt;
            if (id) pinActive(id);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupToc() {&lt;br /&gt;
        if (_isNoTocPage()) return;&lt;br /&gt;
        var toc = document.querySelector(&#039;.vector-toc&#039;);&lt;br /&gt;
        if (!toc) return;&lt;br /&gt;
        removeTocBeginning();&lt;br /&gt;
        removeTocTeekaEntry();&lt;br /&gt;
        renameTocTitle();&lt;br /&gt;
        expandTocSections();&lt;br /&gt;
        injectTocDocNav();&lt;br /&gt;
        attachTocClickHandler(toc);&lt;br /&gt;
        attachHeadingObserver();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        var HIDE_IDS = [&#039;vector-appearance&#039;, &#039;vector-appearance-pinned-container&#039;, &#039;vector-appearance-unpinned-container&#039;];&lt;br /&gt;
        function removeHiddenEls() {&lt;br /&gt;
            HIDE_IDS.forEach(function (id) { var el = document.getElementById(id); if (el &amp;amp;&amp;amp; el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
            var pt = document.getElementById(&#039;vector-page-tools&#039;) || document.querySelector(&#039;.vector-page-tools-pinned-container&#039;);&lt;br /&gt;
            if (pt) pt.querySelectorAll(&#039;[aria-controls=&amp;quot;vector-appearance&amp;quot;]&#039;).forEach(function (el) { if (el.parentNode) el.parentNode.removeChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
        removeHiddenEls();&lt;br /&gt;
        (function detectTeekaMode() {&lt;br /&gt;
            var tp = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
            if (!tp) return;&lt;br /&gt;
            var primary = tp.getAttribute(&#039;data-primary&#039;) || &#039;&#039;;&lt;br /&gt;
            var artPath = (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
            var mainUrl = artPath.replace(&#039;$1&#039;, primary);&lt;br /&gt;
            var refParam = window.location.search.match(/[?&amp;amp;]ref=([01])/);&lt;br /&gt;
            if (refParam) { document.body.classList.add(refParam[1] === &#039;1&#039; ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;); return; }&lt;br /&gt;
            var ref = document.referrer || &#039;&#039;;&lt;br /&gt;
            document.body.classList.add((ref &amp;amp;&amp;amp; primary &amp;amp;&amp;amp; ref.indexOf(mainUrl) !== -1) ? &#039;gr-ref-mode&#039; : &#039;gr-standalone&#039;);&lt;br /&gt;
        }());&lt;br /&gt;
        if (window.MutationObserver) {&lt;br /&gt;
            var hideObs = new MutationObserver(function (mutations) {&lt;br /&gt;
                var dirty = false;&lt;br /&gt;
                mutations.forEach(function (m) { if (m.addedNodes.length) dirty = true; });&lt;br /&gt;
                if (dirty) removeHiddenEls();&lt;br /&gt;
            });&lt;br /&gt;
            hideObs.observe(document.body, { childList: true, subtree: false });&lt;br /&gt;
            setTimeout(function () { hideObs.disconnect(); }, 6000);&lt;br /&gt;
        }&lt;br /&gt;
        var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
        var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
        if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
        else {&lt;br /&gt;
            document.querySelectorAll(&#039;.vector-toc .vector-toc-text:not([data-deva])&#039;).forEach(function (span) {&lt;br /&gt;
                var orig = span.textContent; if (!orig.trim()) return;&lt;br /&gt;
                span.setAttribute(&#039;data-deva&#039;, orig); translatableSpans.push(span);&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        var saved = (function () { try { return localStorage.getItem(LS_SCRIPT_KEY); } catch (e) { return null; } }());&lt;br /&gt;
        if (saved &amp;amp;&amp;amp; saved !== &#039;deva&#039;) { applyScript(saved); } else { currentScript = &#039;deva&#039;; }&lt;br /&gt;
        setTimeout(setupToc, 200);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-script-change&#039;, function (e) {&lt;br /&gt;
        var script = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.script;&lt;br /&gt;
        if (script) applyScript(script);&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;gr-new-content&#039;, function (e) {&lt;br /&gt;
        var container = e &amp;amp;&amp;amp; e.detail &amp;amp;&amp;amp; e.detail.container;&lt;br /&gt;
        if (!container) return;&lt;br /&gt;
        var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        while (walker.nextNode()) nodes.push(walker.currentNode);&lt;br /&gt;
        nodes.forEach(function (node) {&lt;br /&gt;
            var p = node.parentNode;&lt;br /&gt;
            if (!p || (p.hasAttribute &amp;amp;&amp;amp; p.hasAttribute(&#039;data-deva&#039;))) return;&lt;br /&gt;
            var orig = node.textContent;&lt;br /&gt;
            if (!orig.trim()) return;&lt;br /&gt;
            var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
            span.setAttribute(&#039;data-deva&#039;, orig);&lt;br /&gt;
            span.textContent = currentScript !== &#039;deva&#039; ? transliterateText(orig, currentScript) : orig;&lt;br /&gt;
            p.replaceChild(span, node);&lt;br /&gt;
            translatableSpans.push(span);&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        var _grBC = new BroadcastChannel(&#039;gr-script&#039;);&lt;br /&gt;
        _grBC.onmessage = function (e) {&lt;br /&gt;
            var script = e &amp;amp;&amp;amp; e.data &amp;amp;&amp;amp; e.data.script;&lt;br /&gt;
            if (script) { currentScript = script; var sel = document.querySelector(&#039;.gr-script-sel&#039;); if (sel) sel.value = script; applyScript(script); }&lt;br /&gt;
        };&lt;br /&gt;
    } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
                var alreadyTagged = content &amp;amp;&amp;amp; content.querySelector(&#039;[data-deva]&#039;);&lt;br /&gt;
                if (!alreadyTagged) { translatableSpans = []; tagTextNodes(); }&lt;br /&gt;
                else {&lt;br /&gt;
                    document.querySelectorAll(&#039;.vector-toc .vector-toc-text:not([data-deva])&#039;).forEach(function (span) {&lt;br /&gt;
                        var orig = span.textContent; if (!orig.trim()) return;&lt;br /&gt;
                        span.setAttribute(&#039;data-deva&#039;, orig); translatableSpans.push(span);&lt;br /&gt;
                    });&lt;br /&gt;
                }&lt;br /&gt;
                if (currentScript !== &#039;deva&#039;) applyScript(currentScript);&lt;br /&gt;
                setupToc();&lt;br /&gt;
            }, 150);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, init); }&lt;br /&gt;
    else { init(); }&lt;br /&gt;
&lt;br /&gt;
}());   /* ← end of main IIFE */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Inject &amp;quot;Help&amp;quot; and &amp;quot;About&amp;quot; links into the header ─────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
    function makeHeaderLink(id, href, label) {&lt;br /&gt;
        var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
        a.id = id; a.href = href; a.textContent = label;&lt;br /&gt;
        a.className = &#039;gr-header-link&#039;;   // styled in Common.css §37&lt;br /&gt;
        return a;&lt;br /&gt;
    }&lt;br /&gt;
    function injectHeaderLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-about-link&#039;)) return;&lt;br /&gt;
        var headerEnd = document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;);&lt;br /&gt;
        if (!headerEnd) return;&lt;br /&gt;
        var helpLink = makeHeaderLink(&#039;gr-help-link&#039;, wikiHref(&#039;My_wiki:Help&#039;), &#039;Help&#039;);&lt;br /&gt;
        var aboutLink = makeHeaderLink(&#039;gr-about-link&#039;, wikiHref(&#039;My_wiki:About&#039;), &#039;About&#039;);&lt;br /&gt;
&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        var isAnon = !userName || (window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgUserId&#039;) === null);&lt;br /&gt;
&lt;br /&gt;
        var authEl = null;&lt;br /&gt;
        if (isAnon) {&lt;br /&gt;
            if (!document.getElementById(&#039;gr-header-login&#039;)) {&lt;br /&gt;
                authEl = makeHeaderLink(&#039;gr-header-login&#039;, wikiHref(&#039;Special:UserLogin&#039;), &#039;Login&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        } else if (!document.getElementById(&#039;gr-header-user&#039;)) {&lt;br /&gt;
            authEl = buildHeaderUserDropdown(userName);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var ul = document.querySelector(&#039;.vector-user-links&#039;) || document.querySelector(&#039;#pt-userpage&#039;);&lt;br /&gt;
        if (ul &amp;amp;&amp;amp; ul.parentNode === headerEnd) {&lt;br /&gt;
            if (authEl) headerEnd.insertBefore(authEl, ul);&lt;br /&gt;
            headerEnd.insertBefore(aboutLink, authEl || ul);&lt;br /&gt;
            headerEnd.insertBefore(helpLink, aboutLink);&lt;br /&gt;
        } else {&lt;br /&gt;
            headerEnd.appendChild(helpLink);&lt;br /&gt;
            headerEnd.appendChild(aboutLink);&lt;br /&gt;
            if (authEl) headerEnd.appendChild(authEl);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildHeaderUserDropdown(userName) {&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        wrap.id = &#039;gr-header-user&#039;;&lt;br /&gt;
        wrap.style.cssText = &#039;position:relative;display:inline-block;margin-right:4px;&#039;;&lt;br /&gt;
&lt;br /&gt;
        var trigger = document.createElement(&#039;button&#039;);&lt;br /&gt;
        trigger.id = &#039;gr-header-user-trigger&#039;;&lt;br /&gt;
        trigger.type = &#039;button&#039;;&lt;br /&gt;
        trigger.setAttribute(&#039;aria-haspopup&#039;, &#039;true&#039;);&lt;br /&gt;
        trigger.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);&lt;br /&gt;
        trigger.style.cssText = [&lt;br /&gt;
            &#039;display:inline-flex&#039;, &#039;align-items:center&#039;, &#039;gap:5px&#039;,&lt;br /&gt;
            &#039;color:rgba(255,255,255,0.88)&#039;, &#039;font-size:0.88em&#039;,&lt;br /&gt;
            &#039;font-family:system-ui,sans-serif&#039;, &#039;font-weight:500&#039;,&lt;br /&gt;
            &#039;background:transparent&#039;, &#039;border:none&#039;, &#039;cursor:pointer&#039;,&lt;br /&gt;
            &#039;padding:4px 10px&#039;, &#039;border-radius:4px&#039;, &#039;white-space:nowrap&#039;,&lt;br /&gt;
            &#039;transition:color 0.15s,background 0.15s&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
        trigger.innerHTML =&lt;br /&gt;
            &#039;&amp;lt;span&amp;gt;&#039; + userName.replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;span id=&amp;quot;gr-header-caret&amp;quot; style=&amp;quot;font-size:0.8em;transition:transform 0.2s;&amp;quot;&amp;gt;\u25be&amp;lt;/span&amp;gt;&#039;;&lt;br /&gt;
        trigger.addEventListener(&#039;mouseover&#039;, function () { this.style.color = &#039;#fff&#039;; this.style.background = &#039;rgba(255,255,255,0.12)&#039;; });&lt;br /&gt;
        trigger.addEventListener(&#039;mouseout&#039;, function () { if (menu.style.display === &#039;none&#039;) { this.style.color = &#039;rgba(255,255,255,0.88)&#039;; this.style.background = &#039;transparent&#039;; } });&lt;br /&gt;
&lt;br /&gt;
        var menu = document.createElement(&#039;div&#039;);&lt;br /&gt;
        menu.id = &#039;gr-header-user-menu&#039;;&lt;br /&gt;
        menu.setAttribute(&#039;role&#039;, &#039;menu&#039;);&lt;br /&gt;
        menu.style.cssText = [&lt;br /&gt;
            &#039;display:none&#039;, &#039;position:absolute&#039;, &#039;top:100%&#039;, &#039;right:0&#039;, &#039;margin-top:4px&#039;,&lt;br /&gt;
            &#039;min-width:150px&#039;, &#039;background:#fff&#039;, &#039;border-radius:6px&#039;,&lt;br /&gt;
            &#039;box-shadow:0 4px 16px rgba(0,0,0,0.18)&#039;, &#039;overflow:hidden&#039;, &#039;z-index:1000&#039;&lt;br /&gt;
        ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var itemStyle = &#039;display:block;padding:10px 16px;font-size:0.9em;color:#2c1810;text-decoration:none;font-family:system-ui,sans-serif;background:#fff;&#039;;&lt;br /&gt;
        function makeMenuItem(href, label, id) {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = href; a.setAttribute(&#039;role&#039;, &#039;menuitem&#039;);&lt;br /&gt;
            a.textContent = label; a.style.cssText = itemStyle;&lt;br /&gt;
            if (id) a.id = id;&lt;br /&gt;
            a.addEventListener(&#039;mouseover&#039;, function () { this.style.background = &#039;#f5efe9&#039;; });&lt;br /&gt;
            a.addEventListener(&#039;mouseout&#039;, function () { this.style.background = &#039;#fff&#039;; });&lt;br /&gt;
            return a;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        menu.appendChild(makeMenuItem(wikiHref(&#039;Special:Profile&#039;), &#039;Profile&#039;));&lt;br /&gt;
        menu.appendChild(makeMenuItem(&#039;#&#039;, &#039;Log out&#039;, &#039;gr-header-logout&#039;));&lt;br /&gt;
&lt;br /&gt;
        function setOpen(open) {&lt;br /&gt;
            menu.style.display = open ? &#039;block&#039; : &#039;none&#039;;&lt;br /&gt;
            trigger.setAttribute(&#039;aria-expanded&#039;, open ? &#039;true&#039; : &#039;false&#039;);&lt;br /&gt;
            var caret = document.getElementById(&#039;gr-header-caret&#039;);&lt;br /&gt;
            if (caret) caret.style.transform = open ? &#039;rotate(180deg)&#039; : &#039;&#039;;&lt;br /&gt;
            if (open) { trigger.style.color = &#039;#fff&#039;; trigger.style.background = &#039;rgba(255,255,255,0.12)&#039;; }&lt;br /&gt;
            else { trigger.style.color = &#039;rgba(255,255,255,0.88)&#039;; trigger.style.background = &#039;transparent&#039;; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        trigger.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.stopPropagation();&lt;br /&gt;
            setOpen(menu.style.display === &#039;none&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        document.addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            if (!wrap.contains(e.target)) setOpen(false);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        menu.querySelector(&#039;#gr-header-logout&#039;).addEventListener(&#039;click&#039;, function (e) {&lt;br /&gt;
            e.preventDefault();&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.user &amp;amp;&amp;amp; mw.user.tokens) {&lt;br /&gt;
                var t = mw.user.tokens.get(&#039;csrfToken&#039;);&lt;br /&gt;
                if (t) {&lt;br /&gt;
                    var script = (mw.util &amp;amp;&amp;amp; mw.util.wikiScript) ? mw.util.wikiScript() : &#039;/index.php&#039;;&lt;br /&gt;
                    var form = document.createElement(&#039;form&#039;);&lt;br /&gt;
                    form.method = &#039;post&#039;;&lt;br /&gt;
                    form.action = script + &#039;?title=Special:UserLogout&#039;;&lt;br /&gt;
                    form.style.display = &#039;none&#039;;&lt;br /&gt;
                    form.innerHTML =&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;wpEditToken&amp;quot; value=&amp;quot;&#039; + String(t).replace(/&amp;quot;/g, &#039;&amp;amp;quot;&#039;) + &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;Special:UserLogout&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                        &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;returnto&amp;quot; value=&amp;quot;Main Page&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
                    document.body.appendChild(form);&lt;br /&gt;
                    form.submit();&lt;br /&gt;
                    return;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if (window.mw &amp;amp;&amp;amp; mw.Api) {&lt;br /&gt;
                new mw.Api().postWithToken(&#039;csrf&#039;, { action: &#039;logout&#039; })&lt;br /&gt;
                    .done(function () { location.href = &#039;/Main_Page&#039;; })&lt;br /&gt;
                    .fail(function () { location.href = &#039;/index.php?title=Special:UserLogout&#039;; });&lt;br /&gt;
            } else {&lt;br /&gt;
                location.href = &#039;/index.php?title=Special:UserLogout&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        wrap.appendChild(trigger);&lt;br /&gt;
        wrap.appendChild(menu);&lt;br /&gt;
        return wrap;&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, injectHeaderLinks);&lt;br /&gt;
    else injectHeaderLinks();&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Main page: by-Grantha / by-Author toggle ──────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    function grHomeView(v) {&lt;br /&gt;
        var gView = document.getElementById(&#039;gr-view-grantha&#039;);&lt;br /&gt;
        var aView = document.getElementById(&#039;gr-view-author&#039;);&lt;br /&gt;
        var gBtn = document.getElementById(&#039;gr-toggle-grantha&#039;);&lt;br /&gt;
        var aBtn = document.getElementById(&#039;gr-toggle-author&#039;);&lt;br /&gt;
        if (!gView || !aView || !gBtn || !aBtn) return;&lt;br /&gt;
        gView.style.display = (v === &#039;grantha&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        aView.style.display = (v === &#039;author&#039;) ? &#039;&#039; : &#039;none&#039;;&lt;br /&gt;
        gBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;grantha&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        aBtn.className = &#039;gr-toggle-btn&#039; + (v === &#039;author&#039; ? &#039; gr-toggle-active&#039; : &#039;&#039;);&lt;br /&gt;
        try { localStorage.setItem(&#039;gr_home_view&#039;, v); } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ── Ullekha reference link handler ─────────────────────────────────&lt;br /&gt;
(function () {&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightOnArrival() {&lt;br /&gt;
        var search = window.location.search;&lt;br /&gt;
        if (!search) return;&lt;br /&gt;
        var m = search.match(/[?&amp;amp;]hlUllekha=([^&amp;amp;]+)/);&lt;br /&gt;
        if (!m) return;&lt;br /&gt;
        var needle;&lt;br /&gt;
        try { needle = decodeURIComponent(m[1]); } catch (e) { return; }&lt;br /&gt;
        if (!needle || needle.length &amp;lt; 4) return;&lt;br /&gt;
        function doHighlight() {&lt;br /&gt;
            var content = document.querySelector(&#039;.mw-parser-output&#039;);&lt;br /&gt;
            if (!content) return;&lt;br /&gt;
            var snippet = needle.slice(0, 40);&lt;br /&gt;
            var found = false;&lt;br /&gt;
            var spans = content.querySelectorAll(&#039;[data-deva]&#039;);&lt;br /&gt;
            for (var i = 0; i &amp;lt; spans.length &amp;amp;&amp;amp; !found; i++) {&lt;br /&gt;
                var spanEl = spans[i];&lt;br /&gt;
                var orig = spanEl.getAttribute(&#039;data-deva&#039;) || &#039;&#039;;&lt;br /&gt;
                if (orig.indexOf(snippet) === -1) continue;&lt;br /&gt;
                var idx = orig.indexOf(snippet);&lt;br /&gt;
                var hlText = orig.slice(idx, Math.min(idx + needle.length, orig.length));&lt;br /&gt;
                var mark = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                mark.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                mark.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                mark.textContent = hlText;&lt;br /&gt;
                var parent = spanEl.parentNode;&lt;br /&gt;
                if (!parent) continue;&lt;br /&gt;
                var before = document.createTextNode(orig.slice(0, idx));&lt;br /&gt;
                var after = document.createTextNode(orig.slice(idx + hlText.length));&lt;br /&gt;
                parent.insertBefore(before, spanEl); parent.insertBefore(mark, spanEl); parent.insertBefore(after, spanEl); parent.removeChild(spanEl);&lt;br /&gt;
                setTimeout(function () { grScrollTo(mark); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark); }, 100);&lt;br /&gt;
                found = true;&lt;br /&gt;
            }&lt;br /&gt;
            if (!found) {&lt;br /&gt;
                var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);&lt;br /&gt;
                while (walker.nextNode() &amp;amp;&amp;amp; !found) {&lt;br /&gt;
                    var node = walker.currentNode;&lt;br /&gt;
                    var txt = node.textContent || &#039;&#039;;&lt;br /&gt;
                    if (txt.indexOf(snippet) === -1) continue;&lt;br /&gt;
                    var idx2 = txt.indexOf(snippet);&lt;br /&gt;
                    var mark2 = document.createElement(&#039;mark&#039;);&lt;br /&gt;
                    mark2.className = &#039;gr-ullekha-highlight&#039;;&lt;br /&gt;
                    mark2.style.cssText = &#039;background:#fff176;border-radius:2px;padding:0 2px;&#039;;&lt;br /&gt;
                    mark2.textContent = txt.slice(idx2, Math.min(idx2 + needle.length, txt.length));&lt;br /&gt;
                    var p = node.parentNode;&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(0, idx2)), node);&lt;br /&gt;
                    p.insertBefore(mark2, node);&lt;br /&gt;
                    p.insertBefore(document.createTextNode(txt.slice(idx2 + mark2.textContent.length)), node);&lt;br /&gt;
                    p.removeChild(node);&lt;br /&gt;
                    setTimeout(function () { grScrollTo(mark2); if (window.grHighlightTocForElement) window.grHighlightTocForElement(mark2); }, 100);&lt;br /&gt;
                    found = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        doHighlight();&lt;br /&gt;
    }&lt;br /&gt;
    function wireUllekhaLinks() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-ullekha-ref-link&#039;).forEach(function (wrap) {&lt;br /&gt;
            var anchor = wrap.getAttribute(&#039;data-anchor&#039;) || &#039;&#039;;&lt;br /&gt;
            var hl = wrap.getAttribute(&#039;data-hl&#039;) || &#039;&#039;;&lt;br /&gt;
            var a = wrap.querySelector(&#039;a&#039;);&lt;br /&gt;
            if (!a) return;&lt;br /&gt;
            var base = a.href.split(&#039;#&#039;)[0];&lt;br /&gt;
            var encoded = encodeURIComponent(hl);&lt;br /&gt;
            a.href = base + (hl ? &#039;?hlUllekha=&#039; + encoded : &#039;&#039;) + (anchor ? &#039;#&#039; + anchor : &#039;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) { document.addEventListener(&#039;DOMContentLoaded&#039;, function () { highlightOnArrival(); wireUllekhaLinks(); }); }&lt;br /&gt;
    else { highlightOnArrival(); wireUllekhaLinks(); }&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ── Search result highlight ──────────────────────────────────── */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    function storeQueryForLink(url, query) {&lt;br /&gt;
        try {&lt;br /&gt;
            var a = document.createElement(&#039;a&#039;);&lt;br /&gt;
            a.href = url;&lt;br /&gt;
            sessionStorage.setItem(&#039;gr_search_hl&#039;, JSON.stringify({&lt;br /&gt;
                query: query,&lt;br /&gt;
                pathname: a.pathname&lt;br /&gt;
            }));&lt;br /&gt;
        } catch (e) { }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function applyHighlight() {&lt;br /&gt;
        var stored;&lt;br /&gt;
        try {&lt;br /&gt;
            stored = JSON.parse(sessionStorage.getItem(&#039;gr_search_hl&#039;) || &#039;null&#039;);&lt;br /&gt;
        } catch (e) { return; }&lt;br /&gt;
        if (!stored || !stored.query) return;&lt;br /&gt;
&lt;br /&gt;
        var currentPath = window.location.pathname;&lt;br /&gt;
        var storedPath = stored.pathname || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        function normPath(p) { return decodeURIComponent(p).replace(/\/+$/, &#039;&#039;); }&lt;br /&gt;
        if (storedPath &amp;amp;&amp;amp; normPath(storedPath) !== normPath(currentPath)) {&lt;br /&gt;
            try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var query = stored.query.trim();&lt;br /&gt;
        if (!query) return;&lt;br /&gt;
&lt;br /&gt;
        try { sessionStorage.removeItem(&#039;gr_search_hl&#039;); } catch (e) { }&lt;br /&gt;
&lt;br /&gt;
        var delays = [0, 200, 600];&lt;br /&gt;
        delays.forEach(function (ms) {&lt;br /&gt;
            setTimeout(function () {&lt;br /&gt;
                if (document.querySelector(&#039;.gr-search-hl&#039;)) return;&lt;br /&gt;
                highlightText(query);&lt;br /&gt;
            }, ms);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Uses the shared, reflow-proof scroller when it is available.&lt;br /&gt;
    function grScrollTo(el) {&lt;br /&gt;
        if (!el) return;&lt;br /&gt;
        if (window.grScrollToElement) window.grScrollToElement(el);&lt;br /&gt;
        else el.scrollIntoView({ behavior: &#039;smooth&#039;, block: &#039;center&#039; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function highlightText(query) {&lt;br /&gt;
        var content = document.querySelector(&#039;#mw-content-text .mw-parser-output&#039;);&lt;br /&gt;
        if (!content) return;&lt;br /&gt;
&lt;br /&gt;
        var raw = query.replace(/^&amp;quot;|&amp;quot;$/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!raw) return;&lt;br /&gt;
&lt;br /&gt;
        var patterns = [];&lt;br /&gt;
        patterns.push(escapeRegex(raw));&lt;br /&gt;
        raw.split(/\s+/).forEach(function (w) {&lt;br /&gt;
            if (w.length &amp;gt;= 2) patterns.push(escapeRegex(w));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        var matched = false;&lt;br /&gt;
        for (var pi = 0; pi &amp;lt; patterns.length; pi++) {&lt;br /&gt;
            var re;&lt;br /&gt;
            try { re = new RegExp(&#039;(&#039; + patterns[pi] + &#039;)&#039;, &#039;gi&#039;); }&lt;br /&gt;
            catch (e) { continue; }&lt;br /&gt;
            var count = wrapMatches(content, re);&lt;br /&gt;
            if (count &amp;gt; 0) { matched = true; break; }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (!matched) return;&lt;br /&gt;
&lt;br /&gt;
        var first = document.querySelector(&#039;.gr-search-hl&#039;);&lt;br /&gt;
        if (first) {&lt;br /&gt;
            grScrollTo(first);&lt;br /&gt;
            first.classList.add(&#039;gr-search-hl-pulse&#039;);&lt;br /&gt;
            setTimeout(function () { first.classList.remove(&#039;gr-search-hl-pulse&#039;); }, 2000);&lt;br /&gt;
            // NEW: directly trigger the TOC to expand and highlight the&lt;br /&gt;
            // right section, rather than relying solely on the scroll&lt;br /&gt;
            // observer to catch up on its own. This is the actual fix —&lt;br /&gt;
            // confirmed the helper against a realistic multi-section&lt;br /&gt;
            // fragment before wiring it in here.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(first);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        showDismissBar(query);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeRegex(s) {&lt;br /&gt;
        return s.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;amp;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function wrapMatches(root, re) {&lt;br /&gt;
        var count = 0;&lt;br /&gt;
        var walker = document.createTreeWalker(&lt;br /&gt;
            root, NodeFilter.SHOW_TEXT, {&lt;br /&gt;
            acceptNode: function (node) {&lt;br /&gt;
                var p = node.parentElement;&lt;br /&gt;
                if (!p) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                var tag = p.tagName.toUpperCase();&lt;br /&gt;
                if (tag === &#039;SCRIPT&#039; || tag === &#039;STYLE&#039; || tag === &#039;NOSCRIPT&#039;) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                if (p.classList.contains(&#039;gr-search-hl&#039;)) return NodeFilter.FILTER_REJECT;&lt;br /&gt;
                return NodeFilter.FILTER_ACCEPT;&lt;br /&gt;
            }&lt;br /&gt;
        }, false&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        var nodes = [];&lt;br /&gt;
        var node;&lt;br /&gt;
        while ((node = walker.nextNode())) nodes.push(node);&lt;br /&gt;
&lt;br /&gt;
        nodes.forEach(function (textNode) {&lt;br /&gt;
            var val = textNode.nodeValue;&lt;br /&gt;
            if (!re.test(val)) return;&lt;br /&gt;
            re.lastIndex = 0;&lt;br /&gt;
&lt;br /&gt;
            var frag = document.createDocumentFragment();&lt;br /&gt;
            var last = 0;&lt;br /&gt;
            var m;&lt;br /&gt;
            while ((m = re.exec(val)) !== null) {&lt;br /&gt;
                if (m.index &amp;gt; last) {&lt;br /&gt;
                    frag.appendChild(document.createTextNode(val.slice(last, m.index)));&lt;br /&gt;
                }&lt;br /&gt;
                var span = document.createElement(&#039;span&#039;);&lt;br /&gt;
                span.className = &#039;gr-search-hl&#039;;&lt;br /&gt;
                span.textContent = m[0];&lt;br /&gt;
                frag.appendChild(span);&lt;br /&gt;
                last = m.index + m[0].length;&lt;br /&gt;
                count++;&lt;br /&gt;
            }&lt;br /&gt;
            if (last &amp;lt; val.length) {&lt;br /&gt;
                frag.appendChild(document.createTextNode(val.slice(last)));&lt;br /&gt;
            }&lt;br /&gt;
            textNode.parentNode.replaceChild(frag, textNode);&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        return count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showDismissBar(query) {&lt;br /&gt;
        var existing = document.getElementById(&#039;gr-hl-bar&#039;);&lt;br /&gt;
        if (existing) existing.remove();&lt;br /&gt;
&lt;br /&gt;
        var count = document.querySelectorAll(&#039;.gr-search-hl&#039;).length;&lt;br /&gt;
        if (!count) return;&lt;br /&gt;
&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        var bar = document.createElement(&#039;div&#039;);&lt;br /&gt;
        bar.id = &#039;gr-hl-bar&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (isMob) {&lt;br /&gt;
            bar.className = &#039;gr-search-hl-mobile&#039;;&lt;br /&gt;
&lt;br /&gt;
            bar.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-search&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-prev&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Prev&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-next&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Next&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-dismiss&amp;quot; class=&amp;quot;gr-search-bar-btn&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span class=&amp;quot;gra-icon gra-icon-dismiss&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
        } else {&lt;br /&gt;
            bar.style.cssText = [&lt;br /&gt;
                &#039;position:fixed&#039;, &#039;bottom:0&#039;, &#039;left:0&#039;, &#039;right:0&#039;, &#039;z-index:10200&#039;,&lt;br /&gt;
                &#039;background:#b5451b&#039;, &#039;color:#fff&#039;, &#039;padding:10px 16px&#039;,&lt;br /&gt;
                &#039;display:flex&#039;, &#039;align-items:center&#039;, &#039;justify-content:space-between&#039;,&lt;br /&gt;
                &#039;font-family:system-ui,sans-serif&#039;, &#039;font-size:14px&#039;,&lt;br /&gt;
                &#039;box-shadow:0 -2px 8px rgba(0,0,0,0.2)&#039;&lt;br /&gt;
            ].join(&#039;;&#039;);&lt;br /&gt;
&lt;br /&gt;
            var nav = document.createElement(&#039;div&#039;);&lt;br /&gt;
            nav.style.cssText = &#039;display:flex;align-items:center;gap:12px;&#039;;&lt;br /&gt;
            nav.innerHTML =&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;🔍 &amp;lt;strong&amp;gt;&#039; + escHtml(query) + &#039;&amp;lt;/strong&amp;gt; — &#039; + count + &#039; match&#039; + (count === 1 ? &#039;&#039; : &#039;es&#039;) + &#039;&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-prev&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↑ Prev&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-next&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;↓ Next&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button id=&amp;quot;gr-hl-results&amp;quot; style=&amp;quot;background:rgba(255,255,255,0.2);border:none;color:#fff;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&amp;quot;&amp;gt;← Results&amp;lt;/button&amp;gt;&#039;;&lt;br /&gt;
            bar.appendChild(nav);&lt;br /&gt;
&lt;br /&gt;
            var db = document.createElement(&#039;button&#039;);&lt;br /&gt;
            db.id = &#039;gr-hl-dismiss&#039;; db.textContent = &#039;✕ Close&#039;;&lt;br /&gt;
            db.style.cssText = &#039;background:rgba(255,255,255,0.15);border:none;color:#fff;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:13px;min-height:32px;&#039;;&lt;br /&gt;
            bar.appendChild(db);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        document.body.appendChild(bar);&lt;br /&gt;
&lt;br /&gt;
        var hlEls = Array.from(document.querySelectorAll(&#039;.gr-search-hl&#039;));&lt;br /&gt;
        var current = 0;&lt;br /&gt;
&lt;br /&gt;
        function goTo(idx) {&lt;br /&gt;
            hlEls.forEach(function (el) { el.classList.remove(&#039;gr-search-hl-current&#039;); });&lt;br /&gt;
            current = ((idx % hlEls.length) + hlEls.length) % hlEls.length;&lt;br /&gt;
            hlEls[current].classList.add(&#039;gr-search-hl-current&#039;);&lt;br /&gt;
            grScrollTo(hlEls[current]);&lt;br /&gt;
            // NEW: same TOC sync on Prev/Next navigation within the&lt;br /&gt;
            // results, not just the initial jump.&lt;br /&gt;
            if (window.grHighlightTocForElement) window.grHighlightTocForElement(hlEls[current]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        function dismiss() { clearHighlights(); bar.remove(); }&lt;br /&gt;
&lt;br /&gt;
        var nb = document.getElementById(&#039;gr-hl-next&#039;);&lt;br /&gt;
        var pb = document.getElementById(&#039;gr-hl-prev&#039;);&lt;br /&gt;
        var rb = document.getElementById(&#039;gr-hl-results&#039;);&lt;br /&gt;
        var db2 = document.getElementById(&#039;gr-hl-dismiss&#039;);&lt;br /&gt;
        if (nb) nb.onclick = function () { goTo(current + 1); };&lt;br /&gt;
        if (pb) pb.onclick = function () { goTo(current - 1); };&lt;br /&gt;
        if (db2) db2.onclick = dismiss;&lt;br /&gt;
        if (rb) rb.onclick = function () {&lt;br /&gt;
            bar.remove(); clearHighlights();&lt;br /&gt;
            if (window.showSearchDialog) window.showSearchDialog(query);&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearHighlights() {&lt;br /&gt;
        document.querySelectorAll(&#039;.gr-search-hl&#039;).forEach(function (span) {&lt;br /&gt;
            var p = span.parentNode; if (!p) return;&lt;br /&gt;
            while (span.firstChild) p.insertBefore(span.firstChild, span);&lt;br /&gt;
            p.removeChild(span);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escHtml(s) {&lt;br /&gt;
        return String(s).replace(/&amp;amp;/g, &#039;&amp;amp;amp;&#039;).replace(/&amp;lt;/g, &#039;&amp;amp;lt;&#039;).replace(/&amp;gt;/g, &#039;&amp;amp;gt;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectHighlightCSS() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-hl-css&#039;)) return;&lt;br /&gt;
        var s = document.createElement(&#039;style&#039;);&lt;br /&gt;
        s.id = &#039;gr-hl-css&#039;;&lt;br /&gt;
        s.textContent = [&lt;br /&gt;
            &#039;.gr-search-hl{background:#fff176;color:#1a1a1a;border-radius:2px;padding:0 1px;box-shadow:0 0 0 1px rgba(181,69,27,0.25);}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-current{background:#ffb300!important;box-shadow:0 0 0 2px #b5451b!important;}&#039;,&lt;br /&gt;
            &#039;@keyframes gr-hl-pulse{0%{background:#ffb300;}50%{background:#fff176;}100%{background:#fff176;}}&#039;,&lt;br /&gt;
            &#039;.gr-search-hl-pulse{animation:gr-hl-pulse 1.2s ease 2;}&#039;,&lt;br /&gt;
        ].join(&#039;&#039;);&lt;br /&gt;
        document.head.appendChild(s);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    injectHighlightCSS();&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, applyHighlight);&lt;br /&gt;
    } else {&lt;br /&gt;
        applyHighlight();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (window.mw) {&lt;br /&gt;
        mw.hook(&#039;wikipage.content&#039;).add(function () {&lt;br /&gt;
            setTimeout(applyHighlight, 100);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grStoreSearchHL = storeQueryForLink;&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Mobile addon — only runs on Minerva (mobile) skin&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    if (!document.body.classList.contains(&#039;skin-minerva&#039;)) return;&lt;br /&gt;
    function injectMenuLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-menu-items&#039;)) return;&lt;br /&gt;
        var navDrawer = document.querySelector(&#039;.navigation-drawer&#039;);&lt;br /&gt;
        if (!navDrawer) return;&lt;br /&gt;
        var wrap = document.createElement(&#039;div&#039;); wrap.id = &#039;gr-mob-menu-items&#039;; wrap.className = &#039;gr-mob-menu&#039;;&lt;br /&gt;
        function makeItem(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.className = &#039;gr-mob-item&#039;; a.textContent = label; return a; }&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/Main_Page&#039;, &#039;Home&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:Help&#039;, &#039;Help&#039;));&lt;br /&gt;
        wrap.appendChild(makeItem(&#039;/My_wiki:About&#039;, &#039;About&#039;));&lt;br /&gt;
        var userName = window.mw ? mw.config.get(&#039;wgUserName&#039;) : null;&lt;br /&gt;
        if (userName) {&lt;br /&gt;
            wrap.appendChild(makeItem((window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) ? mw.util.getUrl(&#039;Special:Profile&#039;) : &#039;/Special:Profile&#039;, &#039;Profile&#039;));&lt;br /&gt;
            var la = document.querySelector(&#039;a[href*=&amp;quot;action=logout&amp;quot;]&#039;);&lt;br /&gt;
            wrap.appendChild(makeItem(la ? la.href : &#039;/index.php?title=Special:UserLogout&#039;, &#039;Log out&#039;));&lt;br /&gt;
        }&lt;br /&gt;
        else wrap.appendChild(makeItem(&#039;/index.php?title=Special:UserLogin&#039;, &#039;Log in&#039;));&lt;br /&gt;
        var pageLeft = document.getElementById(&#039;mw-mf-page-left&#039;);&lt;br /&gt;
        if (pageLeft) {&lt;br /&gt;
            // Hide Minerva&#039;s own menu lists rather than deleting them: Minerva&#039;s&lt;br /&gt;
            // initMobile.js looks up .toggle-list__list inside this panel and&lt;br /&gt;
            // throws (&amp;quot;Cannot read properties of null (reading &#039;parentNode&#039;)&amp;quot;)&lt;br /&gt;
            // when it is gone, which also stops the rest of Minerva&#039;s setup.&lt;br /&gt;
            Array.prototype.forEach.call(pageLeft.children, function (c) { c.classList.add(&#039;gr-mf-native-menu&#039;); });&lt;br /&gt;
            pageLeft.style.removeProperty(&#039;display&#039;);&lt;br /&gt;
            pageLeft.appendChild(wrap);&lt;br /&gt;
        }&lt;br /&gt;
        else navDrawer.appendChild(wrap);&lt;br /&gt;
    }&lt;br /&gt;
    function injectMoolaUllekhaLinks() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-doc-nav&#039;)) return;&lt;br /&gt;
        var pageName = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgPageName&#039;)) || &#039;&#039;;&lt;br /&gt;
        if (pageName === &#039;Main_Page&#039; || !pageName) return;&lt;br /&gt;
        function wikiUrl(slug) { if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(slug); return ((window.mw &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;).replace(&#039;$1&#039;, encodeURIComponent(slug).replace(/%2F/g, &#039;/&#039;)); }&lt;br /&gt;
        var teekaPage = document.querySelector(&#039;.gr-teeka-page&#039;);&lt;br /&gt;
        var primarySlug = teekaPage ? (teekaPage.getAttribute(&#039;data-primary&#039;) || pageName.split(&#039;/&#039;)[0]) : pageName.split(&#039;/&#039;)[0];&lt;br /&gt;
        var docTitleEl = document.querySelector(&#039;.gr-doc-title&#039;);&lt;br /&gt;
        var hasMoolaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-moola&#039;) === &#039;1&#039;;&lt;br /&gt;
        var hasUllekhaPage = docTitleEl &amp;amp;&amp;amp; docTitleEl.getAttribute(&#039;data-has-ullekha&#039;) === &#039;1&#039;;&lt;br /&gt;
        var showMoolam = !!teekaPage || hasMoolaPage, showUllekha = hasUllekhaPage || !!teekaPage;&lt;br /&gt;
        if (!showMoolam &amp;amp;&amp;amp; !showUllekha) return;&lt;br /&gt;
        var nav = document.createElement(&#039;div&#039;); nav.id = &#039;gr-mob-doc-nav&#039;;&lt;br /&gt;
        nav.className = &#039;toc-main-links&#039;;        // styled in Common.css §24&lt;br /&gt;
        function makeLink(href, label) { var a = document.createElement(&#039;a&#039;); a.href = href; a.textContent = label; a.className = &#039;toc-main-link-item&#039;; return a; }&lt;br /&gt;
        if (teekaPage) nav.appendChild(makeLink(wikiUrl(primarySlug), &#039;मूल&#039;));&lt;br /&gt;
        else if (hasMoolaPage) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Moola&#039;), &#039;मूलम्&#039;));&lt;br /&gt;
        if (showUllekha) nav.appendChild(makeLink(wikiUrl(primarySlug + &#039;/Ullekha&#039;), &#039;उल्लेख&#039;));&lt;br /&gt;
        // Right under the document&#039;s own title, which is what a reader on a&lt;br /&gt;
        // phone actually sees as the title; the skin&#039;s h1 is the fallback.&lt;br /&gt;
        var docTitle = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var h1 = document.getElementById(&#039;firstHeading&#039;) || document.querySelector(&#039;.page-heading, h1.firstHeading, .mw-first-heading&#039;);&lt;br /&gt;
        if (docTitle &amp;amp;&amp;amp; docTitle.parentNode) docTitle.parentNode.insertBefore(nav, docTitle.nextSibling);&lt;br /&gt;
        else if (h1 &amp;amp;&amp;amp; h1.parentNode) h1.parentNode.insertBefore(nav, h1.nextSibling);&lt;br /&gt;
        else { var ct = document.getElementById(&#039;mw-content-text&#039;); if (ct) ct.insertBefore(nav, ct.firstChild); }&lt;br /&gt;
    }&lt;br /&gt;
    function boot() { injectMenuLinks(); injectMoolaUllekhaLinks();}&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    if (window.mw) mw.hook(&#039;wikipage.content&#039;);&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    if (mw.config.get(&#039;wgPageName&#039;) !== &#039;Main_Page&#039;) return;&lt;br /&gt;
    mw.loader.using(&#039;mediawiki.util&#039;).done(function () {&lt;br /&gt;
        $(function () { applyHomeToggleOffset(); window.addEventListener(&#039;resize&#039;, applyHomeToggleOffset, { passive: true }); setTimeout(applyHomeToggleOffset, 300); setTimeout(applyHomeToggleOffset, 800); });&lt;br /&gt;
    });&lt;br /&gt;
    function applyHomeToggleOffset() {&lt;br /&gt;
        var bar = document.getElementById(&#039;gr-static-bar&#039;); if (!bar) return;&lt;br /&gt;
        var barBottom = Math.round(bar.getBoundingClientRect().bottom);&lt;br /&gt;
        [document.getElementById(&#039;gr-home&#039;), document.getElementById(&#039;gr-home-toggle&#039;), document.querySelector(&#039;#mw-content-text .mw-parser-output &amp;gt; .gr-home, #mw-content-text .mw-parser-output &amp;gt; *:first-child&#039;)].forEach(function (el) { if (el) el.style.scrollMarginTop = (barBottom + 4) + &#039;px&#039;; });&lt;br /&gt;
        var isMob = window.innerWidth &amp;lt; 768 || !!document.getElementById(&#039;mw-mf-viewport&#039;);&lt;br /&gt;
        if (isMob) { var toggleEl = document.getElementById(&#039;gr-home-toggle&#039;); if (toggleEl) { var tr = toggleEl.getBoundingClientRect(); if (tr.top &amp;lt; barBottom) { var cp = parseInt(window.getComputedStyle(document.body).paddingTop, 10) || 0; document.body.style.paddingTop = (cp + (barBottom - tr.top) + 4) + &#039;px&#039;; } } }&lt;br /&gt;
    }&lt;br /&gt;
    $(function () {&lt;br /&gt;
        var $toggle = $(&#039;#gr-home-toggle&#039;), $viewG = $(&#039;#gr-view-grantha&#039;), $viewA = $(&#039;#gr-view-author&#039;), $btnG = $(&#039;#gr-toggle-grantha&#039;), $btnA = $(&#039;#gr-toggle-author&#039;);&lt;br /&gt;
        if (!$toggle.length || !$viewG.length || !$viewA.length) return; if ($toggle.data(&#039;gr-wired&#039;)) return; $toggle.data(&#039;gr-wired&#039;, true);&lt;br /&gt;
        function showView(which) { if (which === &#039;grantha&#039;) { $viewG.show(); $viewA.hide(); $btnG.addClass(&#039;gr-toggle-active&#039;); $btnA.removeClass(&#039;gr-toggle-active&#039;); } else { $viewA.show(); $viewG.hide(); $btnA.addClass(&#039;gr-toggle-active&#039;); $btnG.removeClass(&#039;gr-toggle-active&#039;); } try { localStorage.setItem(&#039;grantha_home_tab&#039;, which); } catch (e) { } }&lt;br /&gt;
        $btnG.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;grantha&#039;); });&lt;br /&gt;
        $btnA.on(&#039;click keydown&#039;, function (e) { if (e.type === &#039;keydown&#039; &amp;amp;&amp;amp; e.key !== &#039;Enter&#039; &amp;amp;&amp;amp; e.key !== &#039; &#039;) return; showView(&#039;author&#039;); });&lt;br /&gt;
        try { var saved = localStorage.getItem(&#039;grantha_home_tab&#039;); if (saved === &#039;author&#039;) showView(&#039;author&#039;); else showView(&#039;grantha&#039;); } catch (e) { showView(&#039;grantha&#039;); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
// ── Replace MediaWiki user links with a single username dropdown (Profile + Logout) ──&lt;br /&gt;
(function () {&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(&#039;wgArticlePath&#039;)) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, title);&lt;br /&gt;
    }&lt;br /&gt;
}());&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Commentary interleaving — discovers a document&#039;s /Vyakhya or&lt;br /&gt;
   /Bhashya commentary subpages, lets a reader select which ones to&lt;br /&gt;
   show interleaved with the moola text, and augments any existing&lt;br /&gt;
   link to a commentary (e.g. a &amp;quot;वादावली&amp;quot; list) with its own inline&lt;br /&gt;
   checkbox. Exposes window.grTeekaInterleave so readerToolbar.js&#039;s&lt;br /&gt;
   &amp;quot;Commentaries&amp;quot; dropdown can stay in sync without duplicating this&lt;br /&gt;
   logic — same pattern as window.grHighlightTocForElement above.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
&lt;br /&gt;
    var LS_TEEKA_KEY = &#039;grantha_reader_teeka_in_moola&#039;;&lt;br /&gt;
&lt;br /&gt;
    // Distinct light background per commentary, for when multiple&lt;br /&gt;
    // different teekas are interleaved together and need to stay&lt;br /&gt;
    // visually distinguishable. CHANGED: was a 30-color palette&lt;br /&gt;
    // transcribed from a screenshot - confirmed directly that at least&lt;br /&gt;
    // one entry (#F6C500, a saturated gold) wasn&#039;t actually light at&lt;br /&gt;
    // all, which is very likely what read as &amp;quot;really dark&amp;quot; in practice.&lt;br /&gt;
    // Replaced with 10 colors in the site&#039;s own warm palette (matching&lt;br /&gt;
    // the maroon/cream tones already used throughout Common.css -&lt;br /&gt;
    // #b5451b, #fdf0eb, #f0cfc4), all kept in the ~94-97% lightness&lt;br /&gt;
    // range so they stay easy to read against dark text regardless of&lt;br /&gt;
    // which one a given teeka lands on. First entry is #fcf5f3 - the&lt;br /&gt;
    // exact background .teeka-block itself already uses on a native&lt;br /&gt;
    // teeka page (verified at 97.1% lightness), per direction, so the&lt;br /&gt;
    // most common single-commentary case matches the site&#039;s own&lt;br /&gt;
    // existing look exactly rather than introducing a new shade for it.&lt;br /&gt;
    var TEEKA_COLOR_PALETTE = [&lt;br /&gt;
        &#039;#fcf5f3&#039;, &#039;#F5EFE6&#039;, &#039;#F0F5EC&#039;, &#039;#EEF3F6&#039;, &#039;#F6EFF3&#039;,&lt;br /&gt;
        &#039;#F3F0F8&#039;, &#039;#F8F3EA&#039;, &#039;#EEF6F2&#039;, &#039;#F6EEEE&#039;, &#039;#F2F0EC&#039;&lt;br /&gt;
    ];&lt;br /&gt;
    // Stable djb2-style hash - same teeka slug always maps to the SAME&lt;br /&gt;
    // color everywhere on the site (not re-randomized per page load or&lt;br /&gt;
    // fetch order) - verified directly: a reader learning &amp;quot;this shade =&lt;br /&gt;
    // भावप्रकाशः&amp;quot; needs that to hold site-wide.&lt;br /&gt;
    function teekaColorForSlug(slug) {&lt;br /&gt;
        var h = 5381;&lt;br /&gt;
        for (var i = 0; i &amp;lt; slug.length; i++) h = ((h * 33) ^ slug.charCodeAt(i)) &amp;gt;&amp;gt;&amp;gt; 0;&lt;br /&gt;
        return TEEKA_COLOR_PALETTE[h % TEEKA_COLOR_PALETTE.length];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function readLS(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function writeLS(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
&lt;br /&gt;
    function fetchAllSubpages(rootTitle) {&lt;br /&gt;
        var deferred = $.Deferred(), subpages = [], nsNum = mw.config.get(&#039;wgNamespaceNumber&#039;), localName = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        function ask(apfrom) {&lt;br /&gt;
            var params = { action: &#039;query&#039;, list: &#039;allpages&#039;, apprefix: localName + &#039;/&#039;, apnamespace: nsNum, aplimit: &#039;max&#039;, format: &#039;json&#039; };&lt;br /&gt;
            if (apfrom) params.apcontinue = apfrom;&lt;br /&gt;
            $.ajax({ url: mw.util.wikiScript(&#039;api&#039;), data: params, dataType: &#039;json&#039; }).then(function (data) {&lt;br /&gt;
                var pages = (data.query &amp;amp;&amp;amp; data.query.allpages) || [];&lt;br /&gt;
                pages.forEach(function (p) { subpages.push(p.title); });&lt;br /&gt;
                if (data.continue &amp;amp;&amp;amp; data.continue.apcontinue) ask(data.continue.apcontinue);&lt;br /&gt;
                else deferred.resolve(subpages);&lt;br /&gt;
            }).fail(function () { deferred.resolve(subpages); });&lt;br /&gt;
        }&lt;br /&gt;
        ask(null);&lt;br /&gt;
        return deferred.promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function fetchTeekaJson(title) {&lt;br /&gt;
        return $.ajax({&lt;br /&gt;
            url: mw.util.wikiScript(&#039;api&#039;),&lt;br /&gt;
            data: { action: &#039;query&#039;, titles: &#039;JSON:&#039; + title, prop: &#039;revisions&#039;, rvslots: &#039;main&#039;, rvprop: &#039;content&#039;, format: &#039;json&#039;, formatversion: 2 },&lt;br /&gt;
            dataType: &#039;json&#039;&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            var page = pages[0];&lt;br /&gt;
            if (!page || page.missing) return null;&lt;br /&gt;
            var content = page.revisions &amp;amp;&amp;amp; page.revisions[0] &amp;amp;&amp;amp; page.revisions[0].slots.main.content;&lt;br /&gt;
            if (!content) return null;&lt;br /&gt;
            try { var parsed = JSON.parse(content); parsed._title = title; return parsed; }&lt;br /&gt;
            catch (e) { return null; }&lt;br /&gt;
        }).catch(function () { return null; });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Finds this document&#039;s commentary subpages - filters to the site&#039;s&lt;br /&gt;
    // own established commentary-path convention (Vyakhya/Bhashya are&lt;br /&gt;
    // shown; Moola/Ullekha are never commentary content).&lt;br /&gt;
    function findCommentarySubpages(rootTitle) {&lt;br /&gt;
        return $.Deferred(function (d) {&lt;br /&gt;
            fetchAllSubpages(rootTitle).then(function (all) {&lt;br /&gt;
                d.resolve(all.filter(function (t) {&lt;br /&gt;
                    return t !== rootTitle &amp;amp;&amp;amp; (/\/vyakhya(\/|$)/i.test(t) || /\/bhashya(\/|$)/i.test(t));&lt;br /&gt;
                }));&lt;br /&gt;
            });&lt;br /&gt;
        }).promise();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Builds moola-verse-id -&amp;gt; [{teekaTitle, teekaSlug, text, blockId}].&lt;br /&gt;
    // Handles TWO possible parent-linking shapes so this works across&lt;br /&gt;
    // every commentary document on the site, not just ones from any one&lt;br /&gt;
    // generation pipeline: (1) DIRECT - a type:&amp;quot;teeka&amp;quot; block&#039;s own&lt;br /&gt;
    // &amp;quot;parent&amp;quot; already IS the moola verse id; (2) TWO-HOP - it points to&lt;br /&gt;
    // a type:&amp;quot;verse&amp;quot; block WITHIN THE SAME teeka doc (a quoted-verse&lt;br /&gt;
    // reference), and THAT block&#039;s own parent is the moola verse id.&lt;br /&gt;
    // Deliberately skips anything whose parent is &amp;quot;UNVERIFIED&amp;quot; or&lt;br /&gt;
    // missing, rather than guessing.&lt;br /&gt;
    // CHANGED: also builds a slug -&amp;gt; color-palette-index map here, based&lt;br /&gt;
    // on teekaDocs&#039; OWN ORDER (the order commentaries were actually&lt;br /&gt;
    // discovered/fetched for this page) rather than hashing each slug&lt;br /&gt;
    // independently. Confirmed directly this was the actual issue: hash-&lt;br /&gt;
    // based assignment gives every teeka a color, but nothing guarantees&lt;br /&gt;
    // the FIRST one shown on a page lands on palette slot 0 (#fcf5f3,&lt;br /&gt;
    // the site&#039;s own real .teeka-block default) - it gets whatever slot&lt;br /&gt;
    // its hash happens to produce. Positional assignment makes &amp;quot;first&lt;br /&gt;
    // shown = the exact default color&amp;quot; a guarantee, not a coincidence,&lt;br /&gt;
    // at the cost of a specific teeka&#039;s color now depending on where it&lt;br /&gt;
    // appears in a given page&#039;s own list rather than being fixed&lt;br /&gt;
    // site-wide - a reasonable trade given the direction received.&lt;br /&gt;
    var teekaColorIndexBySlug = {};&lt;br /&gt;
    function buildTeekaInMoolaIndex(teekaDocs) {&lt;br /&gt;
        var index = {};&lt;br /&gt;
        teekaColorIndexBySlug = {};&lt;br /&gt;
        var colorIdx = 0;&lt;br /&gt;
        teekaDocs.forEach(function (td) {&lt;br /&gt;
            if (!td || !td.blocks || !td.doc) return;&lt;br /&gt;
            var slug = td.doc.slug || td._title;&lt;br /&gt;
            if (!(slug in teekaColorIndexBySlug)) {&lt;br /&gt;
                teekaColorIndexBySlug[slug] = colorIdx % TEEKA_COLOR_PALETTE.length;&lt;br /&gt;
                colorIdx++;&lt;br /&gt;
            }&lt;br /&gt;
            var blockById = {};&lt;br /&gt;
            td.blocks.forEach(function (b) { blockById[b.id] = b; });&lt;br /&gt;
            td.blocks.forEach(function (b) {&lt;br /&gt;
                if (b.type !== &#039;teeka&#039;) return;&lt;br /&gt;
                var moolaId = null;&lt;br /&gt;
                if (b.parent &amp;amp;&amp;amp; b.parent !== &#039;UNVERIFIED&#039; &amp;amp;&amp;amp; !blockById[b.parent]) {&lt;br /&gt;
                    moolaId = b.parent;&lt;br /&gt;
                } else {&lt;br /&gt;
                    var verseRef = blockById[b.parent];&lt;br /&gt;
                    if (verseRef &amp;amp;&amp;amp; (verseRef.type === &#039;verse&#039; || verseRef.type === &#039;verse-ref&#039;) &amp;amp;&amp;amp; verseRef.parent &amp;amp;&amp;amp; verseRef.parent !== &#039;UNVERIFIED&#039;) {&lt;br /&gt;
                        moolaId = verseRef.parent;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                if (!moolaId) return;&lt;br /&gt;
                if (!index[moolaId]) index[moolaId] = [];&lt;br /&gt;
                index[moolaId].push({&lt;br /&gt;
                    teekaTitle: td.doc.title || td._title,&lt;br /&gt;
                    teekaSlug: slug,&lt;br /&gt;
                    text: (b.lines || []).join(&#039; &#039;),&lt;br /&gt;
                    blockId: b.id&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        return index;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectTeekaInMoola(index) {&lt;br /&gt;
        var injectedAny = false;&lt;br /&gt;
        Object.keys(index).forEach(function (verseId) {&lt;br /&gt;
            var $verseEl = $(document.getElementById(verseId));&lt;br /&gt;
            if (!$verseEl.length) return;&lt;br /&gt;
            var entries = index[verseId];&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;gr-injected-teeka-group&#039;);&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                // CHANGED: was teekaColorForSlug(slug) (hash-based) -&lt;br /&gt;
                // now looks up the position assigned in&lt;br /&gt;
                // buildTeekaInMoolaIndex, falling back to the hash only&lt;br /&gt;
                // if this slug is somehow missing from that map (should&lt;br /&gt;
                // not normally happen, but safer than an undefined color).&lt;br /&gt;
                var colorIdx = teekaColorIndexBySlug[entry.teekaSlug];&lt;br /&gt;
                var color = (colorIdx !== undefined) ? TEEKA_COLOR_PALETTE[colorIdx] : teekaColorForSlug(entry.teekaSlug);&lt;br /&gt;
                var $entry = $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-block&#039;)&lt;br /&gt;
                    .attr(&#039;data-teeka-slug&#039;, entry.teekaSlug)&lt;br /&gt;
                    .attr(&#039;data-teeka-block-id&#039;, entry.blockId)&lt;br /&gt;
                    .css(&#039;background&#039;, color);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-title&#039;).text(entry.teekaTitle).appendTo($entry);&lt;br /&gt;
                $(&#039;&amp;lt;div&amp;gt;&#039;).addClass(&#039;teeka-body&#039;).html(entry.text).appendTo($entry);&lt;br /&gt;
                $wrap.append($entry);&lt;br /&gt;
            });&lt;br /&gt;
            $verseEl.after($wrap);&lt;br /&gt;
            injectedAny = true;&lt;br /&gt;
        });&lt;br /&gt;
        return injectedAny;&lt;br /&gt;
    }&lt;br /&gt;
    function removeTeekaInMoola() { $(&#039;.gr-injected-teeka-group&#039;).remove(); }&lt;br /&gt;
&lt;br /&gt;
    // CRITICAL FIX: confirmed directly this feature was corrupting saved&lt;br /&gt;
    // page content. QuickEdit is a contenteditable-based editor operating&lt;br /&gt;
    // on the page&#039;s LIVE DOM - since this feature injects real elements&lt;br /&gt;
    // (interleaved teeka blocks, inline checkboxes wrapping links) into&lt;br /&gt;
    // that same DOM, QuickEdit&#039;s save/serialization pipeline had no way&lt;br /&gt;
    // to distinguish them from genuine content, and was saving them back&lt;br /&gt;
    // into the page&#039;s actual wikitext as literal escaped HTML text. This&lt;br /&gt;
    // teardown strips every trace of this feature&#039;s DOM changes -&lt;br /&gt;
    // removing injected teeka blocks entirely, and for inline checkboxes,&lt;br /&gt;
    // specifically UNWRAPPING each link back to its own original&lt;br /&gt;
    // position (not just deleting the wrapper, which would also delete&lt;br /&gt;
    // the link itself) - restoring the DOM to exactly what the page&#039;s&lt;br /&gt;
    // real, saved content actually is, before any editor can see it.&lt;br /&gt;
    function teardownTeekaInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        $(&#039;.gr-teeka-cb-wrap&#039;).each(function () {&lt;br /&gt;
            var $wrap = $(this);&lt;br /&gt;
            var $link = $wrap.find(&#039;a&#039;);&lt;br /&gt;
            if ($link.length) $wrap.before($link);&lt;br /&gt;
            $wrap.remove();&lt;br /&gt;
        });&lt;br /&gt;
        // CHANGED: also strips the gr-teeka-list-item class added to any&lt;br /&gt;
        // &amp;lt;li&amp;gt; for bullet-removal - confirmed via direct test this was&lt;br /&gt;
        // missed initially, which would have left a class attribute&lt;br /&gt;
        // change in the saved content even after the wrapper/checkbox&lt;br /&gt;
        // itself was correctly removed. Also removes the class&lt;br /&gt;
        // ATTRIBUTE entirely (not just the class name) once empty -&lt;br /&gt;
        // classList.remove() alone leaves class=&amp;quot;&amp;quot; behind, which a&lt;br /&gt;
        // byte-for-byte test caught as still not matching the true&lt;br /&gt;
        // original markup (which never had a class attribute at all).&lt;br /&gt;
        $(&#039;.gr-teeka-list-item&#039;).each(function () {&lt;br /&gt;
            this.classList.remove(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            if (!this.className) this.removeAttribute(&#039;class&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Watches for the exact DOM signal that editing has actually begun -&lt;br /&gt;
    // Common.css targets .mw-body-content[contenteditable=&amp;quot;true&amp;quot;]&lt;br /&gt;
    // directly, confirming this is the real, load-order-independent&lt;br /&gt;
    // trigger QuickEdit itself relies on, rather than trying to&lt;br /&gt;
    // intercept every possible UI path that could invoke it (edit&lt;br /&gt;
    // button, keyboard shortcut, or anything else) individually and&lt;br /&gt;
    // risk missing one.&lt;br /&gt;
    function watchForEditMode() {&lt;br /&gt;
        var target = document.querySelector(&#039;.mw-body-content&#039;) || document.body;&lt;br /&gt;
        if (!target || !window.MutationObserver) return;&lt;br /&gt;
        var wasEditing = false;&lt;br /&gt;
        var obs = new MutationObserver(function (mutations) {&lt;br /&gt;
            mutations.forEach(function (m) {&lt;br /&gt;
                if (m.attributeName !== &#039;contenteditable&#039;) return;&lt;br /&gt;
                var isEditingNow = m.target.getAttribute(&#039;contenteditable&#039;) === &#039;true&#039;;&lt;br /&gt;
                if (isEditingNow &amp;amp;&amp;amp; !wasEditing) {&lt;br /&gt;
                    teardownTeekaInterleave();&lt;br /&gt;
                    wasEditing = true;&lt;br /&gt;
                } else if (!isEditingNow &amp;amp;&amp;amp; wasEditing) {&lt;br /&gt;
                    // Editing ended without a page reload (e.g. cancelled) -&lt;br /&gt;
                    // restore using the already-fetched data cached in&lt;br /&gt;
                    // memory, no need to re-fetch anything from the server.&lt;br /&gt;
                    if (teekaIndex) renderInterleave();&lt;br /&gt;
                    if (lastAugmentArgs) {&lt;br /&gt;
                        var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                        if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    }&lt;br /&gt;
                    wasEditing = false;&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
        obs.observe(target, { attributes: true, attributeFilter: [&#039;contenteditable&#039;], subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function normalizeWikiPath(p) {&lt;br /&gt;
        try { p = decodeURIComponent(p); } catch (e) { }&lt;br /&gt;
        return p.replace(/_/g, &#039; &#039;).replace(/\/+$/, &#039;&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── State ──────────────────────────────────────────────────────&lt;br /&gt;
    var selectedTeekaSlugs = {};&lt;br /&gt;
    try { JSON.parse(readLS(LS_TEEKA_KEY) || &#039;[]&#039;).forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }&lt;br /&gt;
    var teekaIndex = null;&lt;br /&gt;
    var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]&lt;br /&gt;
    var lastAugmentArgs = null; // {titles, docs} - cached to restore inline checkboxes after a cancelled edit without re-fetching&lt;br /&gt;
    var checkboxesBySlug = {};&lt;br /&gt;
    var readyCallbacks = [];&lt;br /&gt;
    var changeCallbacks = [];&lt;br /&gt;
    var visibilityCallbacks = [];&lt;br /&gt;
    var isReady = false;&lt;br /&gt;
    var inlineListVisible = null; // null = no inline checkboxes exist at all on this page&lt;br /&gt;
&lt;br /&gt;
    function registerCheckbox(slug, $cb) { (checkboxesBySlug[slug] = checkboxesBySlug[slug] || []).push($cb); }&lt;br /&gt;
&lt;br /&gt;
    function renderInterleave() {&lt;br /&gt;
        removeTeekaInMoola();&lt;br /&gt;
        if (!teekaIndex || !Object.keys(selectedTeekaSlugs).length) return;&lt;br /&gt;
        var filtered = {};&lt;br /&gt;
        Object.keys(teekaIndex).forEach(function (verseId) {&lt;br /&gt;
            var entries = teekaIndex[verseId].filter(function (e) { return selectedTeekaSlugs[e.teekaSlug]; });&lt;br /&gt;
            if (entries.length) filtered[verseId] = entries;&lt;br /&gt;
        });&lt;br /&gt;
        var injectedAny = injectTeekaInMoola(filtered);&lt;br /&gt;
        if (!injectedAny) console.warn(&#039;[grTeekaInterleave] Selected commentaries loaded, but none of their verse ids matched any verse-block on THIS page.&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function toggleSlug(slug, checked) {&lt;br /&gt;
        if (checked) selectedTeekaSlugs[slug] = true; else delete selectedTeekaSlugs[slug];&lt;br /&gt;
        writeLS(LS_TEEKA_KEY, JSON.stringify(Object.keys(selectedTeekaSlugs)));&lt;br /&gt;
        (checkboxesBySlug[slug] || []).forEach(function ($cb) { $cb.prop(&#039;checked&#039;, checked); });&lt;br /&gt;
        renderInterleave();&lt;br /&gt;
        changeCallbacks.forEach(function (cb) { cb(Object.keys(selectedTeekaSlugs)); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Augments existing &amp;lt;a&amp;gt; links pointing to a known commentary subpage&lt;br /&gt;
    // with an inline checkbox, wherever the link already appears -&lt;br /&gt;
    // matched by real href rather than any assumed list markup, since&lt;br /&gt;
    // that markup isn&#039;t something this script controls.&lt;br /&gt;
    // CHANGED: wraps the checkbox AND the link together in one flex&lt;br /&gt;
    // container, rather than just wrapping the checkbox alone next to&lt;br /&gt;
    // an untouched sibling link - confirmed directly (screenshot, twice&lt;br /&gt;
    // now) that aligning a wrapped checkbox against ambient text-&lt;br /&gt;
    // baseline metrics alone still leaves a small vertical offset,&lt;br /&gt;
    // especially with the site&#039;s custom Devanagari font&#039;s own metrics.&lt;br /&gt;
    // Flexbox align-items:center between the checkbox and the link&#039;s&lt;br /&gt;
    // own line box is a more reliable way to center them against EACH&lt;br /&gt;
    // OTHER directly, rather than each independently against the&lt;br /&gt;
    // surrounding line. The link is moved (not cloned) into the&lt;br /&gt;
    // wrapper at its original DOM position, so its href/click behavior&lt;br /&gt;
    // and the surrounding &amp;quot;\u2014 author&amp;quot; text&#039;s sibling order are both&lt;br /&gt;
    // preserved exactly as before.&lt;br /&gt;
    function augmentExistingTeekaLinks(subpageTitles, docs) {&lt;br /&gt;
        var slugByPath = {};&lt;br /&gt;
        subpageTitles.forEach(function (title, i) {&lt;br /&gt;
            var doc = docs[i];&lt;br /&gt;
            if (!doc) return;&lt;br /&gt;
            slugByPath[normalizeWikiPath(mw.util.getUrl(title))] = doc.doc.slug || doc._title;&lt;br /&gt;
        });&lt;br /&gt;
        var $contentLinks = $(&#039;#mw-content-text a[href], .mw-parser-output a[href]&#039;);&lt;br /&gt;
        var augmented = [];&lt;br /&gt;
        $contentLinks.each(function () {&lt;br /&gt;
            var href = this.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
            var path;&lt;br /&gt;
            try { path = normalizeWikiPath(new URL(href, window.location.href).pathname); }&lt;br /&gt;
            catch (e) { return; }&lt;br /&gt;
            var slug = slugByPath[path];&lt;br /&gt;
            if (!slug || this.closest(&#039;.gr-injected-teeka-group&#039;)) return;&lt;br /&gt;
            var li = this.closest(&#039;li&#039;);&lt;br /&gt;
            if (li) li.classList.add(&#039;gr-teeka-list-item&#039;);&lt;br /&gt;
            var $anchor = $(this);&lt;br /&gt;
            var $wrap = $(&#039;&amp;lt;span&amp;gt;&#039;).addClass(&#039;gr-teeka-cb-wrap&#039;);&lt;br /&gt;
            var $cb = $(&#039;&amp;lt;input&amp;gt;&#039;).attr({ type: &#039;checkbox&#039; }).addClass(&#039;gr-teeka-inline-checkbox&#039;).prop(&#039;checked&#039;, !!selectedTeekaSlugs[slug]);&lt;br /&gt;
            $anchor.before($wrap);&lt;br /&gt;
            $wrap.append($cb);&lt;br /&gt;
            $wrap.append($anchor);&lt;br /&gt;
            registerCheckbox(slug, $cb);&lt;br /&gt;
            $cb.on(&#039;change&#039;, function () { toggleSlug(slug, $cb.is(&#039;:checked&#039;)); });&lt;br /&gt;
            augmented.push({ slug: slug, el: $cb[0] });&lt;br /&gt;
        });&lt;br /&gt;
        var missedTitles = subpageTitles.filter(function (t, i) {&lt;br /&gt;
            var d = docs[i]; if (!d) return false;&lt;br /&gt;
            var s = d.doc.slug || d._title;&lt;br /&gt;
            return !augmented.some(function (a) { return a.slug === s; });&lt;br /&gt;
        });&lt;br /&gt;
        if (missedTitles.length) console.warn(&#039;[grTeekaInterleave] No matching link found on this page for: &#039; + missedTitles.join(&#039;, &#039;) + &#039; - the toolbar dropdown still covers them.&#039;);&lt;br /&gt;
        return augmented;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Tracks whether ANY inline checkbox is currently on-screen, and&lt;br /&gt;
    // notifies subscribers only when that overall state actually flips -&lt;br /&gt;
    // verified in isolation before wiring in here. Drives whether&lt;br /&gt;
    // readerToolbar.js&#039;s &amp;quot;Commentaries&amp;quot; control needs to be shown as a&lt;br /&gt;
    // fallback (inline list scrolled out of view) or can stay hidden&lt;br /&gt;
    // (reader can already see and use the inline checkboxes).&lt;br /&gt;
    function observeInlineVisibility(checkboxEls) {&lt;br /&gt;
        if (!checkboxEls.length || !window.IntersectionObserver) return;&lt;br /&gt;
        var visibleSet = new Set();&lt;br /&gt;
        var obs = new IntersectionObserver(function (entries) {&lt;br /&gt;
            entries.forEach(function (entry) {&lt;br /&gt;
                var id = entry.target.__grTeekaId;&lt;br /&gt;
                if (entry.isIntersecting) visibleSet.add(id); else visibleSet.delete(id);&lt;br /&gt;
            });&lt;br /&gt;
            var anyVisible = visibleSet.size &amp;gt; 0;&lt;br /&gt;
            if (anyVisible !== inlineListVisible) {&lt;br /&gt;
                inlineListVisible = anyVisible;&lt;br /&gt;
                visibilityCallbacks.forEach(function (cb) { cb(anyVisible); });&lt;br /&gt;
            }&lt;br /&gt;
        }, { rootMargin: &#039;-60px 0px 0px 0px&#039;, threshold: 0 });&lt;br /&gt;
        checkboxEls.forEach(function (el, i) { el.__grTeekaId = &#039;cb&#039; + i; obs.observe(el); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        // Runs unconditionally, before any of the early-return checks&lt;br /&gt;
        // below - harmless as a no-op teardown on pages where nothing&lt;br /&gt;
        // ever gets injected, but critical to have active on every page&lt;br /&gt;
        // this feature COULD have injected into, regardless of which&lt;br /&gt;
        // exact code path led here.&lt;br /&gt;
        watchForEditMode();&lt;br /&gt;
        var ns = mw.config.get(&#039;wgNamespaceNumber&#039;);&lt;br /&gt;
        var pageName = mw.config.get(&#039;wgPageName&#039;) || &#039;&#039;;&lt;br /&gt;
        var isMainPg = (pageName === &#039;Main_Page&#039; || pageName === &#039;Main Page&#039;);&lt;br /&gt;
        var isTeekaPage = !!document.querySelector(&#039;.gr-teeka-page[data-interleaved=&amp;quot;1&amp;quot;], .gr-parent-ref-block&#039;);&lt;br /&gt;
        if (ns !== 0 || isMainPg || isTeekaPage) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
&lt;br /&gt;
        var rootTitle = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
        findCommentarySubpages(rootTitle).then(function (subpages) {&lt;br /&gt;
            if (!subpages.length) { isReady = true; readyCallbacks.forEach(function (cb) { cb([]); }); return; }&lt;br /&gt;
            $.when.apply($, subpages.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                var fetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                // Pair title+doc together FIRST, then filter as one unit -&lt;br /&gt;
                // filtering `docs` separately from `subpages` and passing&lt;br /&gt;
                // both (now misaligned) arrays elsewhere was a confirmed&lt;br /&gt;
                // real bug in an earlier version of this feature.&lt;br /&gt;
                var pairs = subpages.map(function (title, i) { return { title: title, doc: fetched[i] }; })&lt;br /&gt;
                    .filter(function (p) {&lt;br /&gt;
                        if (!p.doc) console.warn(&#039;[grTeekaInterleave] Could not load JSON:&#039; + p.title);&lt;br /&gt;
                        return !!p.doc;&lt;br /&gt;
                    });&lt;br /&gt;
                var topDocs = pairs.map(function (p) { return p.doc; });&lt;br /&gt;
&lt;br /&gt;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares&lt;br /&gt;
                // its own children via doc.related (an array of slugs,&lt;br /&gt;
                // matching the same field this session&#039;s own generator&lt;br /&gt;
                // already writes into new JSON docs). Only the ASK here&lt;br /&gt;
                // is to show these as sub-categories in the options list&lt;br /&gt;
                // and gate their visibility on the parent being checked&lt;br /&gt;
                // - so this only needs one more level of fetching, not&lt;br /&gt;
                // unbounded recursion.&lt;br /&gt;
                var relatedSlugs = [];&lt;br /&gt;
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });&lt;br /&gt;
                var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });&lt;br /&gt;
&lt;br /&gt;
                function finishInit(subDocsBySlug) {&lt;br /&gt;
                    var allDocs = topDocs.slice();&lt;br /&gt;
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });&lt;br /&gt;
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);&lt;br /&gt;
                    availableList = topDocs.map(function (d) {&lt;br /&gt;
                        var subItems = (d.doc.related || [])&lt;br /&gt;
                            .map(function (s) { return subDocsBySlug[s]; })&lt;br /&gt;
                            .filter(Boolean)&lt;br /&gt;
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });&lt;br /&gt;
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };&lt;br /&gt;
                    });&lt;br /&gt;
                    // Cached at module level so watchForEditMode can&lt;br /&gt;
                    // correctly re-run link augmentation after a&lt;br /&gt;
                    // cancelled edit, without needing to re-fetch&lt;br /&gt;
                    // anything from the server.&lt;br /&gt;
                    lastAugmentArgs = { titles: pairs.map(function (p) { return p.title; }), docs: topDocs };&lt;br /&gt;
                    var augmented = augmentExistingTeekaLinks(lastAugmentArgs.titles, lastAugmentArgs.docs);&lt;br /&gt;
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));&lt;br /&gt;
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point&lt;br /&gt;
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();&lt;br /&gt;
                    isReady = true;&lt;br /&gt;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });&lt;br /&gt;
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if (!uniqueRelated.length) { finishInit({}); return; }&lt;br /&gt;
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {&lt;br /&gt;
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);&lt;br /&gt;
                    var subDocsBySlug = {};&lt;br /&gt;
                    uniqueRelated.forEach(function (slug, i) {&lt;br /&gt;
                        if (!subFetched[i]) console.warn(&#039;[grTeekaInterleave] Could not load sub-commentary JSON:&#039; + slug);&lt;br /&gt;
                        subDocsBySlug[slug] = subFetched[i];&lt;br /&gt;
                    });&lt;br /&gt;
                    finishInit(subDocsBySlug);&lt;br /&gt;
                });&lt;br /&gt;
            });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.grTeekaInterleave = {&lt;br /&gt;
        teardown: teardownTeekaInterleave,&lt;br /&gt;
        onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },&lt;br /&gt;
        onChange: function (cb) { changeCallbacks.push(cb); },&lt;br /&gt;
        onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },&lt;br /&gt;
        getSelected: function () { return Object.keys(selectedTeekaSlugs); },&lt;br /&gt;
        toggle: function (slug, checked) { toggleSlug(slug, checked); },&lt;br /&gt;
        registerCheckbox: registerCheckbox&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, init);&lt;br /&gt;
    else init();&lt;br /&gt;
&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Devanagari page titles, site-wide  (window.grTitles)&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   One source of truth for &amp;quot;what is this page called&amp;quot;: the Main Page&lt;br /&gt;
   catalogue, where every published work and commentary is listed as&lt;br /&gt;
   [[Slug|देवनागरी नाम]]. update_main_page.py keeps that list current,&lt;br /&gt;
   so a newly pushed commentary gets its Devanagari name everywhere&lt;br /&gt;
   without anyone editing a hard-coded list.&lt;br /&gt;
&lt;br /&gt;
   Used by:&lt;br /&gt;
     • readerToolbar.js search results (grDocDisplayTitle &amp;amp; co. ask&lt;br /&gt;
       grTitles first, then fall back to their own GR_DOC_TITLES map)&lt;br /&gt;
     • the Updates page&lt;br /&gt;
     • this block itself, which relabels any link on the page whose&lt;br /&gt;
       text is still the English slug (e.g. the &amp;quot;Vadavali · Bhavavivaranam&amp;quot;&lt;br /&gt;
       page-nav line), the mobile page heading, and the browser tab title.&lt;br /&gt;
&lt;br /&gt;
   The catalogue is cached in localStorage for CACHE_HOURS and refreshed&lt;br /&gt;
   in the background, so lookups are synchronous on almost every view.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_titles_v1&#039;;&lt;br /&gt;
    var CACHE_HOURS = 1;&lt;br /&gt;
    var DEVA = /[ऀ-ॿ]/;&lt;br /&gt;
&lt;br /&gt;
    var full = {};     // &amp;quot;Katha/Vyakhya/VyasatirthaVivarana&amp;quot; → &amp;quot;श्रीव्यासतीर्थविरचितविवरणम्&amp;quot;&lt;br /&gt;
    var leaf = {};     // &amp;quot;VyasatirthaVivarana&amp;quot; → same; null when two pages share a leaf&lt;br /&gt;
    var waiting = [];&lt;br /&gt;
    var loaded = false;&lt;br /&gt;
&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function add(title, label, override) {&lt;br /&gt;
        title = norm(title); label = String(label || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        if (!title || !label || !DEVA.test(label)) return;&lt;br /&gt;
        if (override || !full[title]) full[title] = label;&lt;br /&gt;
        var l = title.split(&#039;/&#039;).pop();&lt;br /&gt;
        if (!(l in leaf) || override) leaf[l] = label;&lt;br /&gt;
        else if (leaf[l] !== label) leaf[l] = null;   // ambiguous leaf: don&#039;t guess&lt;br /&gt;
    }&lt;br /&gt;
    function parseCatalogue(txt) {&lt;br /&gt;
        var out = {}, re = /\[\[([^\]|#]+)\|([^\]]+)\]\]/g, m;&lt;br /&gt;
        while ((m = re.exec(txt || &#039;&#039;))) {&lt;br /&gt;
            var k = norm(m[1]);&lt;br /&gt;
            if (!out[k]) out[k] = m[2].replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
        }&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
    function useCatalogue(map) {&lt;br /&gt;
        full = {}; leaf = {};&lt;br /&gt;
        Object.keys(map).forEach(function (k) { add(k, map[k]); });&lt;br /&gt;
        addCurrentPage();&lt;br /&gt;
    }&lt;br /&gt;
    // The page being viewed names itself in .gr-doc-title — covers a page&lt;br /&gt;
    // that isn&#039;t on the Main Page yet.&lt;br /&gt;
    function addCurrentPage() {&lt;br /&gt;
        var el = document.querySelector(&#039;#mw-content-text .gr-doc-title&#039;);&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (el &amp;amp;&amp;amp; me &amp;amp;&amp;amp; !full[me]) add(me, el.textContent);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── public API ────────────────────────────────────────────────&lt;br /&gt;
    function get(title) { return full[norm(title)] || null; }&lt;br /&gt;
    function getLeaf(seg) { var v = leaf[norm(seg)]; return v || null; }&lt;br /&gt;
    function whenReady(cb) { if (loaded) cb(); else waiting.push(cb); }&lt;br /&gt;
    function all() { var o = {}; for (var k in full) o[k] = full[k]; return o; }&lt;br /&gt;
    window.grTitles = { get: get, leaf: getLeaf, all: all, whenReady: whenReady, parse: parseCatalogue };&lt;br /&gt;
&lt;br /&gt;
    function finish() {&lt;br /&gt;
        loaded = true;&lt;br /&gt;
        var cbs = waiting; waiting = [];&lt;br /&gt;
        cbs.forEach(function (cb) { try { cb(); } catch (e) { } });&lt;br /&gt;
        relabel();&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;gr.titles&#039;).fire(window.grTitles);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── relabel what&#039;s on screen ──────────────────────────────────&lt;br /&gt;
    function articleTitleFromHref(href) {&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    function relabel(root) {&lt;br /&gt;
        root = root || document;&lt;br /&gt;
        var links = root.querySelectorAll(&#039;#mw-content-text a[href], .gr-sticky-title a[href], #mw-panel a[href], .vector-toc a[href]&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            if (a.children.length) return;                         // leave rich links alone&lt;br /&gt;
            var text = norm(a.textContent);&lt;br /&gt;
            if (!text || DEVA.test(text)) return;                   // already Devanagari&lt;br /&gt;
            var target = articleTitleFromHref(a.getAttribute(&#039;href&#039;)) || norm(a.getAttribute(&#039;title&#039;));&lt;br /&gt;
            if (!target) return;&lt;br /&gt;
            var tail = target.split(&#039;/&#039;).pop();&lt;br /&gt;
            if (text !== target &amp;amp;&amp;amp; text !== tail) return;           // only replace a bare slug&lt;br /&gt;
            var label = get(target) || (text === tail ? getLeaf(tail) : null);&lt;br /&gt;
            if (label) a.textContent = label;&lt;br /&gt;
        });&lt;br /&gt;
        // Page heading (shown on mobile) and the browser tab&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        var myLabel = get(me);&lt;br /&gt;
        if (!myLabel) return;&lt;br /&gt;
        var h = document.querySelector(&#039;#firstHeading .mw-page-title-main&#039;) || document.getElementById(&#039;firstHeading&#039;);&lt;br /&gt;
        if (h &amp;amp;&amp;amp; !h.children.length &amp;amp;&amp;amp; norm(h.textContent) === me) h.textContent = myLabel;&lt;br /&gt;
        var wgTitle = norm(cfg(&#039;wgTitle&#039;));&lt;br /&gt;
        if (wgTitle &amp;amp;&amp;amp; document.title.indexOf(wgTitle) === 0) document.title = myLabel + document.title.slice(wgTitle.length);&lt;br /&gt;
        else if (document.title.indexOf(me) === 0) document.title = myLabel + document.title.slice(me.length);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── load ─────────────────────────────────────────────────────&lt;br /&gt;
    function fetchFresh() {&lt;br /&gt;
        return fetch(script(&#039;index&#039;) + &#039;?title=&#039; + CATALOGUE_PAGE + &#039;&amp;amp;action=raw&#039;, { credentials: &#039;same-origin&#039; })&lt;br /&gt;
            .then(function (r) { return r.ok ? r.text() : Promise.reject(); })&lt;br /&gt;
            .then(function (txt) {&lt;br /&gt;
                var map = parseCatalogue(txt);&lt;br /&gt;
                lsSet(CACHE_KEY, JSON.stringify({ at: Date.now(), map: map }));&lt;br /&gt;
                return map;&lt;br /&gt;
            });&lt;br /&gt;
    }&lt;br /&gt;
    function boot() {&lt;br /&gt;
        var cached = null;&lt;br /&gt;
        try { cached = JSON.parse(lsGet(CACHE_KEY) || &#039;null&#039;); } catch (e) { }&lt;br /&gt;
        if (cached &amp;amp;&amp;amp; cached.map) { useCatalogue(cached.map); finish(); }&lt;br /&gt;
        var stale = !cached || (Date.now() - cached.at &amp;gt; CACHE_HOURS * 3600000);&lt;br /&gt;
        if (!stale) return;&lt;br /&gt;
        fetchFresh().then(function (map) {&lt;br /&gt;
            useCatalogue(map);&lt;br /&gt;
            if (!loaded) finish(); else relabel();&lt;br /&gt;
        }).catch(function () { if (!loaded) { addCurrentPage(); finish(); } });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Content that arrives later (previews, lazy sections, search panes)&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function ($c) {&lt;br /&gt;
        if (loaded) { addCurrentPage(); relabel($c &amp;amp;&amp;amp; $c[0] ? $c[0] : document); }&lt;br /&gt;
    });&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Header links &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot;, and the Updates page&lt;br /&gt;
   ───────────────────────────────────────────────────────────────&lt;br /&gt;
   Desktop (Vector): &amp;quot;Library&amp;quot; and &amp;quot;Updates&amp;quot; added before &amp;quot;Help&amp;quot;.&lt;br /&gt;
   Mobile (Minerva): the same two items in the menu panel, after &amp;quot;Home&amp;quot;.&lt;br /&gt;
   &amp;quot;Updates&amp;quot; opens the page My_wiki:Updates, which has two parts:&lt;br /&gt;
&lt;br /&gt;
   1. What&#039;s new — filled in automatically from the wiki&#039;s edit history&lt;br /&gt;
      into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt;. Nothing to maintain by hand:&lt;br /&gt;
        • New      a page created in the last WINDOW_DAYS days&lt;br /&gt;
        • Updated  an existing page whose edits in that window add up to&lt;br /&gt;
                   at least UPDATE_MIN_BYTES (so typo fixes don&#039;t show)&lt;br /&gt;
      Titles are the Devanagari names from window.grTitles (Main Page catalogue).&lt;br /&gt;
      Test pages, helper subpages (/Ullekha, /Moola, /C1 …) and deleted&lt;br /&gt;
      pages are left out.&lt;br /&gt;
   2. Announcements &amp;amp; events — a card editors fill by hand with&lt;br /&gt;
      {{Bulletin|type=…|date=…|title=…|text=…}} lines on that page.&lt;br /&gt;
&lt;br /&gt;
   Styling: MediaWiki:Common.css §37 (no inline styles here).&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var LIBRARY_URL = &#039;https://dvaita-grantha-hierarchy-library.ai.studio/&#039;;&lt;br /&gt;
    var LIBRARY_LABEL = &#039;Library&#039;;&lt;br /&gt;
    var UPDATES_LABEL = &#039;Updates&#039;;&lt;br /&gt;
    var UPDATES_PAGE = &#039;My_wiki:Updates&#039;;   // the page the &amp;quot;Updates&amp;quot; link opens&lt;br /&gt;
    var WINDOW_DAYS = 90;          // how far back the list looks&lt;br /&gt;
    var UPDATE_MIN_BYTES = 1500;   // edits smaller than this in total aren&#039;t &amp;quot;updates&amp;quot;&lt;br /&gt;
    var MAX_ITEMS = 100;           // rows shown on the page&lt;br /&gt;
    var CACHE_MINUTES = 10;        // per-tab cache so every page view isn&#039;t an API call&lt;br /&gt;
    var CATALOGUE_PAGE = &#039;Main_Page&#039;;&lt;br /&gt;
    var SKIP_TITLE = /^(Main Page|Test|Sandbox)|(^|\/)(Ullekha|Moola|C\d+)$|(^|\/)(Test|Teeka|Tippani)\d*$/i;&lt;br /&gt;
    var HIDE = [];                 // exact titles never to list, e.g. [&#039;Sumadhwavijaya/Vyakhya/पदार्थदीपिका&#039;]&lt;br /&gt;
&lt;br /&gt;
    var SEEN_KEY = &#039;gr_updates_seen&#039;;&lt;br /&gt;
    var CACHE_KEY = &#039;gr_updates_cache_v3&#039;;&lt;br /&gt;
    var isMobile = document.body.classList.contains(&#039;skin-minerva&#039;);&lt;br /&gt;
&lt;br /&gt;
    // ── helpers ────────────────────────────────────────────────&lt;br /&gt;
    function cfg(k) { return (window.mw &amp;amp;&amp;amp; mw.config &amp;amp;&amp;amp; mw.config.get(k)) || null; }&lt;br /&gt;
    function wikiHref(title) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.getUrl) return mw.util.getUrl(title);&lt;br /&gt;
        var ap = cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;;&lt;br /&gt;
        return ap.replace(&#039;$1&#039;, encodeURIComponent(title.replace(/ /g, &#039;_&#039;)).replace(/%2F/g, &#039;/&#039;).replace(/%3A/g, &#039;:&#039;));&lt;br /&gt;
    }&lt;br /&gt;
    function script(name) {&lt;br /&gt;
        if (window.mw &amp;amp;&amp;amp; mw.util &amp;amp;&amp;amp; mw.util.wikiScript) return mw.util.wikiScript(name);&lt;br /&gt;
        return (cfg(&#039;wgScriptPath&#039;) || &#039;&#039;) + &#039;/&#039; + name + &#039;.php&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    function lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function ssGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }&lt;br /&gt;
    function ssSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) { } }&lt;br /&gt;
    function el(tag, cls, text) {&lt;br /&gt;
        var n = document.createElement(tag);&lt;br /&gt;
        if (cls) n.className = cls;&lt;br /&gt;
        if (text != null) n.textContent = text;&lt;br /&gt;
        return n;&lt;br /&gt;
    }&lt;br /&gt;
    function norm(t) { return String(t || &#039;&#039;).replace(/_/g, &#039; &#039;).replace(/\s+/g, &#039; &#039;).trim(); }&lt;br /&gt;
&lt;br /&gt;
    var MONTHS = [&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];&lt;br /&gt;
    function absDate(d) { return MONTHS[d.getMonth()] + &#039; &#039; + d.getDate() + &#039;, &#039; + d.getFullYear(); }&lt;br /&gt;
    // Today · 1 day ago · 5 days ago (under a week) · Sep 16, 2026&lt;br /&gt;
    function relDate(d, now) {&lt;br /&gt;
        now = now || new Date();&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days === 1) return &#039;1 day ago&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return days + &#039; days ago&#039;;&lt;br /&gt;
        return absDate(d);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── data ───────────────────────────────────────────────────&lt;br /&gt;
    function getJSON(url) {&lt;br /&gt;
        return fetch(url, { credentials: &#039;same-origin&#039; }).then(function (r) {&lt;br /&gt;
            if (!r.ok) throw new Error(&#039;HTTP &#039; + r.status);&lt;br /&gt;
            return r.json();&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function api(params) {&lt;br /&gt;
        var q = [&#039;format=json&#039;, &#039;formatversion=2&#039;];&lt;br /&gt;
        for (var k in params) q.push(k + &#039;=&#039; + encodeURIComponent(params[k]));&lt;br /&gt;
        return getJSON(script(&#039;api&#039;) + &#039;?&#039; + q.join(&#039;&amp;amp;&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Devanagari names come from window.grTitles (the Main Page catalogue,&lt;br /&gt;
    // cached) — the same names search and links use.&lt;br /&gt;
    function fetchCatalogue() {&lt;br /&gt;
        return new Promise(function (resolve) {&lt;br /&gt;
            if (window.grTitles) window.grTitles.whenReady(function () { resolve(window.grTitles.all()); });&lt;br /&gt;
            else resolve({});&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Every main-namespace change in the window, oldest→newest pages grouped.&lt;br /&gt;
    function fetchChanges() {&lt;br /&gt;
        var since = new Date(Date.now() - WINDOW_DAYS * 86400000).toISOString().replace(/\.\d+Z$/, &#039;Z&#039;);&lt;br /&gt;
        var all = [];&lt;br /&gt;
        function page(cont) {&lt;br /&gt;
            var p = {&lt;br /&gt;
                action: &#039;query&#039;, list: &#039;recentchanges&#039;, rctype: &#039;new|edit&#039;, rcnamespace: 0,&lt;br /&gt;
                rcshow: &#039;!redirect&#039;, rcprop: &#039;title|timestamp|sizes&#039;, rclimit: 500, rcend: since&lt;br /&gt;
            };&lt;br /&gt;
            if (cont) p.rccontinue = cont;&lt;br /&gt;
            return api(p).then(function (j) {&lt;br /&gt;
                all = all.concat((j.query &amp;amp;&amp;amp; j.query.recentchanges) || []);&lt;br /&gt;
                // Two pages (1,000 changes) is plenty for a notifications list.&lt;br /&gt;
                if (j[&#039;continue&#039;] &amp;amp;&amp;amp; j[&#039;continue&#039;].rccontinue &amp;amp;&amp;amp; all.length &amp;lt; 1000) return page(j[&#039;continue&#039;].rccontinue);&lt;br /&gt;
                return all;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
        return page(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function summarise(changes, catalogue) {&lt;br /&gt;
        var byTitle = {};&lt;br /&gt;
        changes.forEach(function (c) {&lt;br /&gt;
            var t = norm(c.title);&lt;br /&gt;
            if (SKIP_TITLE.test(t) || HIDE.indexOf(t) !== -1) return;&lt;br /&gt;
            var e = byTitle[t] || (byTitle[t] = { title: t, created: null, last: null, bytes: 0 });&lt;br /&gt;
            var ts = new Date(c.timestamp);&lt;br /&gt;
            if (c.type === &#039;new&#039;) e.created = ts;&lt;br /&gt;
            else e.bytes += Math.abs((c.newlen || 0) - (c.oldlen || 0));&lt;br /&gt;
            if (!e.last || ts &amp;gt; e.last) e.last = ts;&lt;br /&gt;
        });&lt;br /&gt;
        var out = [];&lt;br /&gt;
        Object.keys(byTitle).forEach(function (t) {&lt;br /&gt;
            var e = byTitle[t];&lt;br /&gt;
            var listed = !!catalogue[t];&lt;br /&gt;
            // Pages the catalogue doesn&#039;t know about still count if they look like&lt;br /&gt;
            // real content: a top-level work or a commentary under /Vyakhya/.&lt;br /&gt;
            if (!listed &amp;amp;&amp;amp; t.indexOf(&#039;/&#039;) !== -1 &amp;amp;&amp;amp; !/\/Vyakhya\/[^/]+$/.test(t)) return;&lt;br /&gt;
            if (e.created) out.push({ title: t, kind: &#039;new&#039;, when: e.created });&lt;br /&gt;
            else if (e.bytes &amp;gt;= UPDATE_MIN_BYTES) out.push({ title: t, kind: &#039;update&#039;, when: e.last });&lt;br /&gt;
        });&lt;br /&gt;
        out.forEach(function (x) { x.listed = !!catalogue[x.title]; x.label = labelFor(x.title, catalogue); x.sub = subFor(x.title, catalogue); });&lt;br /&gt;
        return out.sort(function (a, b) { return b.when - a.when; });&lt;br /&gt;
    }&lt;br /&gt;
    function labelFor(t, cat) {&lt;br /&gt;
        if (cat[t]) return cat[t];&lt;br /&gt;
        var parts = t.split(&#039;/&#039;);&lt;br /&gt;
        return parts[parts.length - 1];&lt;br /&gt;
    }&lt;br /&gt;
    // Second line: which work a commentary belongs to.&lt;br /&gt;
    function subFor(t, cat) {&lt;br /&gt;
        var m = /^([^/]+)\/Vyakhya\//.exec(t);&lt;br /&gt;
        if (!m) return &#039;&#039;;&lt;br /&gt;
        return cat[m[1]] ? cat[m[1]] + &#039; — टीका&#039; : &#039;टीका&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Drop pages created in the window and deleted since.&lt;br /&gt;
    function dropMissing(items) {&lt;br /&gt;
        if (!items.length) return Promise.resolve(items);&lt;br /&gt;
        var batches = [];&lt;br /&gt;
        for (var i = 0; i &amp;lt; items.length; i += 50) batches.push(items.slice(i, i + 50));&lt;br /&gt;
        return Promise.all(batches.map(function (b) {&lt;br /&gt;
            return api({ action: &#039;query&#039;, prop: &#039;info&#039;, inprop: &#039;displaytitle&#039;, titles: b.map(function (x) { return x.title; }).join(&#039;|&#039;) })&lt;br /&gt;
                .then(function (j) {&lt;br /&gt;
                    var gone = {}, shown = {};&lt;br /&gt;
                    ((j.query &amp;amp;&amp;amp; j.query.pages) || []).forEach(function (p) {&lt;br /&gt;
                        if (p.missing) gone[norm(p.title)] = true;&lt;br /&gt;
                        var dt = (p.displaytitle || &#039;&#039;).replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, &#039;&#039;).trim();&lt;br /&gt;
                        if (dt &amp;amp;&amp;amp; norm(dt) !== norm(p.title)) shown[norm(p.title)] = dt;&lt;br /&gt;
                    });&lt;br /&gt;
                    return b.filter(function (x) {&lt;br /&gt;
                        // A DISPLAYTITLE only helps pages the catalogue doesn&#039;t name.&lt;br /&gt;
                        if (shown[x.title] &amp;amp;&amp;amp; !x.listed) x.label = shown[x.title];&lt;br /&gt;
                        return !gone[x.title];&lt;br /&gt;
                    });&lt;br /&gt;
                }).catch(function () { return b; });&lt;br /&gt;
        })).then(function (r) { return [].concat.apply([], r); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadUpdates() {&lt;br /&gt;
        var cached = ssGet(CACHE_KEY);&lt;br /&gt;
        if (cached) {&lt;br /&gt;
            try {&lt;br /&gt;
                var c = JSON.parse(cached);&lt;br /&gt;
                if (Date.now() - c.at &amp;lt; CACHE_MINUTES * 60000) {&lt;br /&gt;
                    return Promise.resolve(c.items.map(function (x) { x.when = new Date(x.when); return x; }));&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) { }&lt;br /&gt;
        }&lt;br /&gt;
        return Promise.all([fetchChanges(), fetchCatalogue()]).then(function (r) {&lt;br /&gt;
            return dropMissing(summarise(r[0], r[1]).slice(0, MAX_ITEMS + 10));&lt;br /&gt;
        }).then(function (items) {&lt;br /&gt;
            items = items.slice(0, MAX_ITEMS);&lt;br /&gt;
            ssSet(CACHE_KEY, JSON.stringify({ at: Date.now(), items: items }));&lt;br /&gt;
            return items;&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    function lastSeen() { return +lsGet(SEEN_KEY) || 0; }&lt;br /&gt;
    function markSeen() { lsSet(SEEN_KEY, String(Date.now())); }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the automatic list ─────────────────────────&lt;br /&gt;
    //    Filled into &amp;lt;div id=&amp;quot;gr-updates-feed&amp;quot;&amp;gt; on UPDATES_PAGE.&lt;br /&gt;
    //    All styling lives in Common.css §37.&lt;br /&gt;
    var KIND = { &#039;new&#039;: [&#039;New&#039;, &#039;gr-upd-new&#039;], &#039;update&#039;: [&#039;Updated&#039;, &#039;gr-upd-update&#039;] };&lt;br /&gt;
&lt;br /&gt;
    // Today · This week · This month · then one group per month (&amp;quot;August 2026&amp;quot;)&lt;br /&gt;
    var MONTHS_LONG = [&#039;January&#039;, &#039;February&#039;, &#039;March&#039;, &#039;April&#039;, &#039;May&#039;, &#039;June&#039;, &#039;July&#039;, &#039;August&#039;, &#039;September&#039;, &#039;October&#039;, &#039;November&#039;, &#039;December&#039;];&lt;br /&gt;
    function groupName(d, now) {&lt;br /&gt;
        var a = new Date(d.getFullYear(), d.getMonth(), d.getDate());&lt;br /&gt;
        var b = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var days = Math.round((b - a) / 86400000);&lt;br /&gt;
        if (days &amp;lt;= 0) return &#039;Today&#039;;&lt;br /&gt;
        if (days &amp;lt; 7) return &#039;This week&#039;;&lt;br /&gt;
        if (d.getFullYear() === now.getFullYear() &amp;amp;&amp;amp; d.getMonth() === now.getMonth()) return &#039;Earlier this month&#039;;&lt;br /&gt;
        return MONTHS_LONG[d.getMonth()] + &#039; &#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderFeed(box, items, seenBefore) {&lt;br /&gt;
        while (box.firstChild) box.removeChild(box.firstChild);&lt;br /&gt;
        if (items === null) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;Loading…&#039;)); return; }&lt;br /&gt;
        if (!items.length) { box.appendChild(el(&#039;p&#039;, &#039;gr-upd-empty&#039;, &#039;No new or updated pages in the last &#039; + WINDOW_DAYS + &#039; days.&#039;)); return; }&lt;br /&gt;
        var now = new Date(), group = null, list = null;&lt;br /&gt;
        items.forEach(function (x) {&lt;br /&gt;
            var g = groupName(x.when, now);&lt;br /&gt;
            if (g !== group) {&lt;br /&gt;
                group = g;&lt;br /&gt;
                // A div, not an h3: the site&#039;s heading rules would restyle an h3.&lt;br /&gt;
                var h = el(&#039;div&#039;, &#039;gr-upd-group&#039;, g);&lt;br /&gt;
                h.setAttribute(&#039;role&#039;, &#039;heading&#039;); h.setAttribute(&#039;aria-level&#039;, &#039;3&#039;);&lt;br /&gt;
                box.appendChild(h);&lt;br /&gt;
                list = el(&#039;div&#039;, &#039;gr-upd-list&#039;);&lt;br /&gt;
                list.setAttribute(&#039;role&#039;, &#039;list&#039;);&lt;br /&gt;
                box.appendChild(list);&lt;br /&gt;
            }&lt;br /&gt;
            var li = el(&#039;div&#039;, &#039;gr-upd-item&#039; + (seenBefore &amp;amp;&amp;amp; x.when.getTime() &amp;gt; seenBefore ? &#039; gr-upd-unseen&#039; : &#039;&#039;));&lt;br /&gt;
            li.setAttribute(&#039;role&#039;, &#039;listitem&#039;);&lt;br /&gt;
            var main = el(&#039;div&#039;, &#039;gr-upd-main&#039;);&lt;br /&gt;
            var a = el(&#039;a&#039;, &#039;gr-upd-title&#039;, x.label);&lt;br /&gt;
            a.href = wikiHref(x.title);&lt;br /&gt;
            main.appendChild(a);&lt;br /&gt;
            var sub = el(&#039;div&#039;, &#039;gr-upd-sub&#039;);&lt;br /&gt;
            sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-tag &#039; + KIND[x.kind][1], KIND[x.kind][0]));&lt;br /&gt;
            if (x.sub) sub.appendChild(el(&#039;span&#039;, &#039;gr-upd-work&#039;, x.sub));&lt;br /&gt;
            main.appendChild(sub);&lt;br /&gt;
            li.appendChild(main);&lt;br /&gt;
            var d = el(&#039;time&#039;, &#039;gr-upd-date&#039;, relDate(x.when, now));&lt;br /&gt;
            d.setAttribute(&#039;datetime&#039;, x.when.toISOString());&lt;br /&gt;
            d.title = absDate(x.when);&lt;br /&gt;
            li.appendChild(d);&lt;br /&gt;
            list.appendChild(li);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── Updates page: the bulletin card ─────────────────────────&lt;br /&gt;
    //    Items are written on the page with {{Bulletin|…}}; this only&lt;br /&gt;
    //    formats their dates, marks past events, and shows an empty note.&lt;br /&gt;
    function decorateBulletin() {&lt;br /&gt;
        var card = document.querySelector(&#039;.gr-bulletin&#039;);&lt;br /&gt;
        if (!card) return;&lt;br /&gt;
        var now = new Date(), today = new Date(now.getFullYear(), now.getMonth(), now.getDate());&lt;br /&gt;
        var items = card.querySelectorAll(&#039;.gr-bulletin-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(items, function (it) {&lt;br /&gt;
            var raw = it.getAttribute(&#039;data-date&#039;) || &#039;&#039;;&lt;br /&gt;
            var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());&lt;br /&gt;
            var d = m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;&lt;br /&gt;
            var slot = it.querySelector(&#039;.gr-bulletin-date&#039;);&lt;br /&gt;
            if (d &amp;amp;&amp;amp; slot) { slot.textContent = absDate(d); slot.setAttribute(&#039;title&#039;, raw); }&lt;br /&gt;
            // Past events stay listed, dimmed; hide them with until= on the item.&lt;br /&gt;
            if (d &amp;amp;&amp;amp; it.getAttribute(&#039;data-type&#039;) === &#039;event&#039; &amp;amp;&amp;amp; d &amp;lt; today) it.classList.add(&#039;gr-bulletin-past&#039;);&lt;br /&gt;
            var until = /^(\d{4})-(\d{2})-(\d{2})$/.exec((it.getAttribute(&#039;data-until&#039;) || &#039;&#039;).trim());&lt;br /&gt;
            if (until &amp;amp;&amp;amp; new Date(+until[1], +until[2] - 1, +until[3]) &amp;lt; today) it.classList.add(&#039;gr-bulletin-expired&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        var visible = card.querySelectorAll(&#039;.gr-bulletin-item:not(.gr-bulletin-expired)&#039;).length;&lt;br /&gt;
        card.classList.toggle(&#039;gr-bulletin-is-empty&#039;, visible === 0);&lt;br /&gt;
        var layout = card.closest(&#039;.gr-updates-layout&#039;);&lt;br /&gt;
        if (layout) layout.classList.toggle(&#039;gr-no-bulletin&#039;, visible === 0);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initUpdatesPage() {&lt;br /&gt;
        var box = document.getElementById(&#039;gr-updates-feed&#039;);&lt;br /&gt;
        decorateBulletin();&lt;br /&gt;
        if (!box || box.getAttribute(&#039;data-ready&#039;)) return;&lt;br /&gt;
        box.setAttribute(&#039;data-ready&#039;, &#039;1&#039;);&lt;br /&gt;
        var seenBefore = lastSeen();&lt;br /&gt;
        renderFeed(box, null);&lt;br /&gt;
        loadUpdates().then(function (items) {&lt;br /&gt;
            renderFeed(box, items, seenBefore);&lt;br /&gt;
            markSeen();&lt;br /&gt;
        }).catch(function () { renderFeed(box, []); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── header links ─────────────────────────────────────────────&lt;br /&gt;
    function updatesHref() { return wikiHref(UPDATES_PAGE); }&lt;br /&gt;
&lt;br /&gt;
    // Which wiki page a link points at (&amp;quot;My wiki:Help&amp;quot;), or null.&lt;br /&gt;
    function linkTarget(a) {&lt;br /&gt;
        var href = a.getAttribute(&#039;href&#039;) || &#039;&#039;;&lt;br /&gt;
        if (!href || href.charAt(0) === &#039;#&#039;) return null;   // e.g. the Log out button&lt;br /&gt;
        var u;&lt;br /&gt;
        try { u = new URL(href, location.href); } catch (e) { return null; }&lt;br /&gt;
        if (u.origin !== location.origin) return null;&lt;br /&gt;
        var t = u.searchParams.get(&#039;title&#039;);&lt;br /&gt;
        if (!t) {&lt;br /&gt;
            var ap = (cfg(&#039;wgArticlePath&#039;) || &#039;/wiki/$1&#039;).split(&#039;$1&#039;)[0];&lt;br /&gt;
            if (u.pathname.indexOf(ap) !== 0) return null;&lt;br /&gt;
            t = u.pathname.slice(ap.length);&lt;br /&gt;
        }&lt;br /&gt;
        try { t = decodeURIComponent(t); } catch (e) { }&lt;br /&gt;
        return norm(t);&lt;br /&gt;
    }&lt;br /&gt;
    // Highlight whichever header / menu link is the page being viewed —&lt;br /&gt;
    // Help, About, Updates, Profile … not only the ones this block adds.&lt;br /&gt;
    function markCurrent() {&lt;br /&gt;
        var me = norm(cfg(&#039;wgPageName&#039;));&lt;br /&gt;
        if (!me) return;&lt;br /&gt;
        var links = document.querySelectorAll(&#039;.gr-header-link, #gr-header-user-menu a, .gr-mob-item&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(links, function (a) {&lt;br /&gt;
            var on = a.tagName === &#039;A&#039; &amp;amp;&amp;amp; linkTarget(a) === me;&lt;br /&gt;
            a.classList.toggle(&#039;is-current&#039;, on);&lt;br /&gt;
            if (on) a.setAttribute(&#039;aria-current&#039;, &#039;page&#039;); else a.removeAttribute(&#039;aria-current&#039;);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectDesktop() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-library-link&#039;)) return true;&lt;br /&gt;
        var help = document.getElementById(&#039;gr-help-link&#039;);&lt;br /&gt;
        var host = help ? help.parentNode :&lt;br /&gt;
            (document.querySelector(&#039;.vector-header-end&#039;) || document.querySelector(&#039;#vector-user-links&#039;) || document.querySelector(&#039;.mw-header&#039;));&lt;br /&gt;
        if (!host) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-header-link&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-library-link&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
        lib.title = &#039;Grantha catalogue (opens in a new tab)&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-header-link&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-updates-link&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        if (help) { host.insertBefore(lib, help); host.insertBefore(upd, help); }&lt;br /&gt;
        else { host.appendChild(lib); host.appendChild(upd); }&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function injectMobile() {&lt;br /&gt;
        if (document.getElementById(&#039;gr-mob-library&#039;)) return true;&lt;br /&gt;
        var menu = document.getElementById(&#039;gr-mob-menu-items&#039;);&lt;br /&gt;
        if (!menu) return false;&lt;br /&gt;
&lt;br /&gt;
        var lib = el(&#039;a&#039;, &#039;gr-mob-item&#039;, LIBRARY_LABEL);&lt;br /&gt;
        lib.id = &#039;gr-mob-library&#039;;&lt;br /&gt;
        lib.href = LIBRARY_URL; lib.target = &#039;_blank&#039;; lib.rel = &#039;noopener noreferrer&#039;;&lt;br /&gt;
&lt;br /&gt;
        var upd = el(&#039;a&#039;, &#039;gr-mob-item&#039;, UPDATES_LABEL);&lt;br /&gt;
        upd.id = &#039;gr-mob-updates&#039;;&lt;br /&gt;
        upd.href = updatesHref();&lt;br /&gt;
&lt;br /&gt;
        var home = null;&lt;br /&gt;
        for (var i = 0; i &amp;lt; menu.children.length; i++) {&lt;br /&gt;
            if (/^\s*Home\s*$/.test(menu.children[i].textContent)) { home = menu.children[i]; break; }&lt;br /&gt;
        }&lt;br /&gt;
        var ref = home ? home.nextSibling : menu.firstChild;&lt;br /&gt;
        menu.insertBefore(lib, ref);&lt;br /&gt;
        menu.insertBefore(upd, ref);&lt;br /&gt;
        markCurrent();&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // ── boot: the Help/About and mobile-menu code creates the anchors on&lt;br /&gt;
    //    DOMContentLoaded, so wait briefly for them rather than assume order.&lt;br /&gt;
    function boot() {&lt;br /&gt;
        initUpdatesPage();&lt;br /&gt;
        var inject = isMobile ? injectMobile : injectDesktop;&lt;br /&gt;
        if (inject()) return;&lt;br /&gt;
        var tries = 0;&lt;br /&gt;
        var obs = window.MutationObserver ? new MutationObserver(function () { if (inject()) obs.disconnect(); }) : null;&lt;br /&gt;
        if (obs) obs.observe(document.body, { childList: true, subtree: true });&lt;br /&gt;
        (function poll() {&lt;br /&gt;
            if (inject()) { if (obs) obs.disconnect(); return; }&lt;br /&gt;
            if (++tries &amp;lt; 40) setTimeout(poll, 250); else if (obs) obs.disconnect();&lt;br /&gt;
        }());&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, boot);&lt;br /&gt;
    else boot();&lt;br /&gt;
    // Page previews and live-preview re-render content; re-run the page part then.&lt;br /&gt;
    if (window.mw &amp;amp;&amp;amp; mw.hook) mw.hook(&#039;wikipage.content&#039;).add(function () { initUpdatesPage(); });&lt;br /&gt;
&lt;br /&gt;
    window.grUpdates = { relDate: relDate, summarise: summarise, groupName: groupName };&lt;br /&gt;
}());&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ═══════════════════════════════════════════════════════════════&lt;br /&gt;
   Main Page cards: item count beside each title, and a&lt;br /&gt;
   .gr-home-card-wide class on long lists. Styling: Common.css §25.&lt;br /&gt;
   ═══════════════════════════════════════════════════════════════ */&lt;br /&gt;
(function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    var WIDE_AT = 14;   // cards with this many items or more span two columns&lt;br /&gt;
    function decorate() {&lt;br /&gt;
        var cards = document.querySelectorAll(&#039;.gr-home .gr-home-card&#039;);&lt;br /&gt;
        Array.prototype.forEach.call(cards, function (card) {&lt;br /&gt;
            var title = card.querySelector(&#039;.gr-home-card-title&#039;);&lt;br /&gt;
            var n = card.querySelectorAll(&#039;li&#039;).length;&lt;br /&gt;
            if (!n) return;&lt;br /&gt;
            if (n &amp;gt;= WIDE_AT) card.classList.add(&#039;gr-home-card-wide&#039;);&lt;br /&gt;
            if (!title || title.querySelector(&#039;.gr-home-card-count&#039;)) return;&lt;br /&gt;
            var label = document.createElement(&#039;span&#039;);&lt;br /&gt;
            while (title.firstChild) label.appendChild(title.firstChild);&lt;br /&gt;
            title.appendChild(label);&lt;br /&gt;
            var c = document.createElement(&#039;span&#039;);&lt;br /&gt;
            c.className = &#039;gr-home-card-count&#039;;&lt;br /&gt;
            c.textContent = String(n);&lt;br /&gt;
            title.appendChild(c);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) document.addEventListener(&#039;DOMContentLoaded&#039;, decorate);&lt;br /&gt;
    else decorate();&lt;br /&gt;
}());&lt;/div&gt;</summary>
		<author><name>Vaishnavi</name></author>
	</entry>
</feed>